For any given set, for instance,
val fruits = Set("apple", "grape", "pear", "banana")
how to get a random element from fruits
?
Many Thanks.
For any given set, for instance,
val fruits = Set("apple", "grape", "pear", "banana")
how to get a random element from fruits
?
Many Thanks.
convert into Vector
and get random element from it
scala> val fruits = Set("apple", "grape", "pear", "banana")
fruits: scala.collection.immutable.Set[String] = Set(apple, grape, pear, banana)
scala> import scala.util.Random
import scala.util.Random
scala> val rnd=new Random
rnd: scala.util.Random = scala.util.Random@31a9253
scala> fruits.toVector(rnd.nextInt(fruits.size))
res8: String = apple
So, every answer posted before has complexity O(n) in terms of space, since they create a copy a whole collection in some way. Here is a solution without any additional copying (therefore it is "constant space"):
def random[T](s: Set[T]): T = {
val n = util.Random.nextInt(s.size)
s.iterator.drop(n).next
}
You can directly access an element of a Set with slice. I used this when I was working with a set that was changing in size, so converting it to a Vector every time seemed like overkill.
val roll = new Random ()
val n = roll nextInt (fruits size)
fruits slice (n, n + 1) last
Solution1
Random way ( import scala.util.Random
)
scala> fruits.toList(Random.nextInt(fruits.size))
res0: java.lang.String = banana
Solution2
Math way (no imports)
scala> fruits.toList((math.random*fruits.size).toInt)
res1: String = banana
Drawing inspiration from the other answers to this question, I've come up with:
private def randomItem[T](items: Traversable[T]): Option[T] = {
val i = Random.nextInt(items.size)
items.view(i, i + 1).headOption
}
This doesn't copy anything, doesn't fail if the Set
(or other type of Traversable
) is empty, and it's clear at a glance what it does. If you're certain that the Set
is not empty, you could use .head
instead of headOption
, returning a T
.
import Scala.util.Random
val fruits = Set("apple", "grape", "pear", "banana").toVector
val sz =fruits.size
val num = Random.nextInt(sz)
fruits(num)
Not converting the Set
to an ordered collection but using zipWithIndex
we can attribute an index to each item in the collection,
fruits.zipWithIndex
Set((apple,0), (grape,1), (pear,2), (banana,3))
Thus for val rnd = util.Random.nextInt(fruits.size)
,
fruits.zipWithIndex.find( _._2 == rnd)
Option[(String, Int)] = Some((banana,3))
Given an empty set,
Set[String]().zipWithIndex.find( _._2 == 3)
Option[(String, Int)] = None
If you don't mind an O(n)
solution:
import util.Random
// val fruits = Set("apple", "grape", "pear", "banana")
Random.shuffle(fruits).head
// "pear"