Extension random() to select random element from a collection

Fixes #KT-15539
This commit is contained in:
Ilya Gorbunov
2018-08-24 06:12:50 +03:00
parent b0bcd78e38
commit ea2c33a532
20 changed files with 652 additions and 3 deletions
@@ -16,6 +16,7 @@ package kotlin.collections
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns 1st *element* from the collection.
@@ -2049,6 +2050,213 @@ public inline fun CharArray.lastOrNull(predicate: (Char) -> Boolean): Char? {
return null
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun <T> Array<out T>.random(): T {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun ByteArray.random(): Byte {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun ShortArray.random(): Short {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun IntArray.random(): Int {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun LongArray.random(): Long {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun FloatArray.random(): Float {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun DoubleArray.random(): Double {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun BooleanArray.random(): Boolean {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun CharArray.random(): Char {
return random(Random)
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun <T> Array<out T>.random(random: Random): T {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun ByteArray.random(random: Random): Byte {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun ShortArray.random(random: Random): Short {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun IntArray.random(random: Random): Int {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun LongArray.random(random: Random): Long {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun FloatArray.random(random: Random): Float {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun DoubleArray.random(random: Random): Double {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun BooleanArray.random(random: Random): Boolean {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
public fun CharArray.random(random: Random): Char {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns the single element, or throws an exception if the array is empty or has more than one element.
*/
@@ -16,6 +16,7 @@ package kotlin.collections
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns 1st *element* from the collection.
@@ -456,6 +457,29 @@ public inline fun <T> List<T>.lastOrNull(predicate: (T) -> Boolean): T? {
return null
}
/**
* Returns a random element from this collection.
*
* @throws NoSuchElementException if this collection is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun <T> Collection<T>.random(): T {
return random(Random)
}
/**
* Returns a random element from this collection using the specified source of randomness.
*
* @throws NoSuchElementException if this collection is empty.
*/
@SinceKotlin("1.3")
public fun <T> Collection<T>.random(random: Random): T {
if (isEmpty())
throw NoSuchElementException("Collection is empty.")
return elementAt(random.nextInt(size))
}
/**
* Returns the single element, or throws an exception if the collection is empty or has more than one element.
*/
@@ -16,6 +16,7 @@ package kotlin.comparisons
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns the greater of two values.
@@ -16,6 +16,7 @@ package kotlin.collections
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns a [List] containing all key-value pairs.
@@ -16,6 +16,70 @@ package kotlin.ranges
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns a random element from this range.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun IntRange.random(): Int {
return random(Random)
}
/**
* Returns a random element from this range.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun LongRange.random(): Long {
return random(Random)
}
/**
* Returns a random element from this range.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun CharRange.random(): Char {
return random(Random)
}
/**
* Returns a random element from this range using the specified source of randomness.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
public fun IntRange.random(random: Random): Int {
return random.nextInt(this)
}
/**
* Returns a random element from this range using the specified source of randomness.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
public fun LongRange.random(random: Random): Long {
return random.nextLong(this)
}
/**
* Returns a random element from this range using the specified source of randomness.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
public fun CharRange.random(random: Random): Char {
return random.nextInt(first.toInt(), last.toInt() + 1).toChar()
}
/**
* Checks if the specified [value] belongs to this range.
@@ -16,6 +16,7 @@ package kotlin.sequences
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
import kotlin.coroutines.experimental.*
/**
@@ -16,6 +16,7 @@ package kotlin.collections
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns a set containing all elements of the original set except the given [element].
@@ -16,6 +16,7 @@ package kotlin.text
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns a character at the given [index] or throws an [IndexOutOfBoundsException] if the [index] is out of bounds of this char sequence.
@@ -170,6 +171,29 @@ public inline fun CharSequence.lastOrNull(predicate: (Char) -> Boolean): Char? {
return null
}
/**
* Returns a random character from this char sequence.
*
* @throws NoSuchElementException if this char sequence is empty.
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun CharSequence.random(): Char {
return random(Random)
}
/**
* Returns a random character from this char sequence using the specified source of randomness.
*
* @throws NoSuchElementException if this char sequence is empty.
*/
@SinceKotlin("1.3")
public fun CharSequence.random(random: Random): Char {
if (isEmpty())
throw NoSuchElementException("Char sequence is empty.")
return get(random.nextInt(length))
}
/**
* Returns the single character, or throws an exception if the char sequence is empty or has more than one character.
*/
@@ -16,6 +16,107 @@ package kotlin.collections
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly
public inline fun UIntArray.random(): UInt {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly
public inline fun ULongArray.random(): ULong {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly
public inline fun UByteArray.random(): UByte {
return random(Random)
}
/**
* Returns a random element from this array.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly
public inline fun UShortArray.random(): UShort {
return random(Random)
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
public fun UIntArray.random(random: Random): UInt {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
public fun ULongArray.random(random: Random): ULong {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
public fun UByteArray.random(random: Random): UByte {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns a random element from this array using the specified source of randomness.
*
* @throws NoSuchElementException if this array is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
public fun UShortArray.random(random: Random): UShort {
if (isEmpty())
throw NoSuchElementException("Array is empty.")
return get(random.nextInt(size))
}
/**
* Returns an array of type [ByteArray], which is a view of this array where each element is a signed reinterpretation
@@ -16,6 +16,53 @@ package kotlin.ranges
import kotlin.*
import kotlin.text.*
import kotlin.comparisons.*
import kotlin.random.*
/**
* Returns a random element from this range.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly
public inline fun UIntRange.random(): UInt {
return random(Random)
}
/**
* Returns a random element from this range.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly
public inline fun ULongRange.random(): ULong {
return random(Random)
}
/**
* Returns a random element from this range using the specified source of randomness.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
public fun UIntRange.random(random: Random): UInt {
return random.nextUInt(this)
}
/**
* Returns a random element from this range using the specified source of randomness.
*
* @throws IllegalArgumentException if this range is empty.
*/
@SinceKotlin("1.3")
@ExperimentalUnsignedTypes
public fun ULongRange.random(random: Random): ULong {
return random.nextULong(this)
}
/**
* Returns a progression from this value down to the specified [to] value with the step -1.
@@ -11,6 +11,7 @@ import test.collections.behaviors.*
import test.comparisons.STRING_CASE_INSENSITIVE_ORDER
import kotlin.test.*
import kotlin.comparisons.*
import kotlin.random.Random
fun <T> assertArrayNotSameButEquals(expected: Array<out T>, actual: Array<out T>, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) }
fun assertArrayNotSameButEquals(expected: IntArray, actual: IntArray, message: String = "") { assertTrue(expected !== actual && expected contentEquals actual, message) }
@@ -592,6 +593,31 @@ class ArraysTest {
expect(2) { arrayOf(1, 2, 3).last { it % 2 == 0 } }
}
@Test fun random() {
Array(100) { it }.let { array ->
val tosses = List(10) { array.random() }
assertTrue(tosses.distinct().size > 1, "Should be some distinct elements in $tosses")
val seed = Random.nextInt()
val random1 = Random(seed)
val random2 = Random(seed)
val tosses1 = List(10) { array.random(random1) }
val tosses2 = List(10) { array.random(random2) }
assertEquals(tosses1, tosses2)
}
arrayOf("x").let { singletonArray ->
val tosses = List(10) { singletonArray.random() }
assertEquals(singletonArray.toList(), tosses.distinct())
}
assertFailsWith<NoSuchElementException> { emptyArray<Any>().random() }
}
@Test fun contains() {
assertTrue(arrayOf("1", "2", "3", "4").contains("2"))
assertTrue("3" in arrayOf("1", "2", "3", "4"))
@@ -10,6 +10,8 @@ import kotlin.test.*
import test.collections.behaviors.*
import test.comparisons.STRING_CASE_INSENSITIVE_ORDER
import kotlin.comparisons.*
import kotlin.math.sin
import kotlin.random.Random
class CollectionTest {
@@ -620,6 +622,31 @@ class CollectionTest {
assertFails { arrayListOf<Int>().last() }
}
@Test fun random() {
val list = List(100) { it }
val set = list.toSet()
listOf(list, set).forEach { collection: Collection<Int> ->
val tosses = List(10) { collection.random() }
assertTrue(tosses.distinct().size > 1, "Should be some distinct elements in $tosses")
val seed = Random.nextInt()
val random1 = Random(seed)
val random2 = Random(seed)
val tosses1 = List(10) { collection.random(random1) }
val tosses2 = List(10) { collection.random(random2) }
assertEquals(tosses1, tosses2)
}
listOf("x").let { singletonList ->
val tosses = List(10) { singletonList.random() }
assertEquals(singletonList, tosses.distinct())
}
assertFailsWith<NoSuchElementException> { emptyList<Any>().random() }
}
@Test fun subscript() {
val list = arrayListOf("foo", "bar")
assertEquals("foo", list[0])
+24
View File
@@ -10,6 +10,7 @@ import test.*
import test.collections.behaviors.iteratorBehavior
import test.collections.compare
import kotlin.math.sign
import kotlin.random.Random
fun createString(content: String): CharSequence = content
@@ -936,6 +937,29 @@ class StringTest {
assertNull(data.filterNot { it.isAsciiLetter() || it.isAsciiDigit() }.firstOrNull())
}
@Test fun random() = withOneCharSequenceArg { data ->
data("abcdefg").let { charSeq ->
val tosses = List(10) { charSeq.random() }
assertTrue(tosses.distinct().size > 1, "Should be some distinct elements in $tosses")
val seed = Random.nextInt()
val random1 = Random(seed)
val random2 = Random(seed)
val tosses1 = List(10) { charSeq.random(random1) }
val tosses2 = List(10) { charSeq.random(random2) }
assertEquals(tosses1, tosses2)
}
data("x").let { singletonCharSeq ->
val tosses = List(10) { singletonCharSeq.random() }
assertEquals(singletonCharSeq.toList(), tosses.distinct())
}
assertFailsWith<NoSuchElementException> { data("").random() }
}
@Test fun partition() {
val data = "a1b2c3"
val pair = data.partition { it.isAsciiDigit() }