Accepted answer

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


Not converting the Set to an ordered collection but using zipWithIndex we can attribute an index to each item in the collection,

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")
// "pear"


   import Scala.util.Random

   val fruits = Set("apple", "grape", "pear", "banana").toVector

   val sz =fruits.size

   val num = Random.nextInt(sz)




Random way ( import scala.util.Random )

scala>  fruits.toList(Random.nextInt(fruits.size))
res0: java.lang.String = banana


Math way (no imports)

scala> fruits.toList((math.random*fruits.size).toInt)
res1: String = banana


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


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.


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)

Related Query

More Query from same tag