57

For example, there is a Scala array val A = Array("please", "help", "me"). How to choose a random element from this array?

6 Answers 6

94
import scala.util.Random

val A = Array("please", "help", "me")
Random.shuffle(A.toList).head
Sign up to request clarification or add additional context in comments.

5 Comments

is it efficient to re-order the whole list and pick the head than just picking in O(1) a value of the array?
c2.com/cgi/wiki?PrematureOptimization The question does not specify any efficiency needs, and actually gives an example of a very short Array :)
Why should it be temporary? It works, and is less prone to any coding error in the array bounds.
Ought to be headOption, since the Seq (Array here) might be empty.
A is not empty. It contains three strings.
44
import scala.util.Random

val A = List(1, 2, 3, 4, 5, 6)
A(Random.nextInt(A.size))

3 Comments

this will crash if A.size equals to zero. Never forget about edge cases
Oh, you can use A.lift(Random.nextInt(A.size)) which will give you Option[Int]
@JonOnstott also will crash if A.size is 0
39
import java.util.Random
// ...
val rand = new Random(System.currentTimeMillis())
val random_index = rand.nextInt(A.length)
val result = A(random_index)

4 Comments

while the purpose here is insecure pseudo-randomness, it is bad practice to self-seed a RNG unless you really, really know what you are doing. Setting to current time is particularly bad as it significantly narrows the window of possible seeds. For more info see this series: jazzy.id.au/default/2010/09/20/…
scala.util.Random has shuffle, which is much simpler, especially if you need multiple random elements. See answer below.
this will crash if A.length equals to zero. Never forget about edge cases
This edge case is inherent in the requirement.
5

We can also add some safety with the Option monad (using the lift method)

Actually, when using this method on any collection, even if your collection is empty, or your random index is out of boundaries, your result will always be an Option.

Drive safe <3

def getRandElemO[T](arr: Array[T]): Option[T] =
  if (arr.isEmpty) None
  else arr.lift(util.Random.nextInt(arr.length))

// or the one liner:
// def getRandElemO[T](arr: Array[T]): Option[T] =
//   arr.headOption.flatMap(_ => arr.lift(util.Random.nextInt(arr.length)))

1 Comment

Note that it is not technically referencially transparent since two calls with the same parameter won't give the same result. To achieve this you would have to pass the RNG instance in params as well.
3

If you want a more idiomatic solution, consider using the typeclass pattern (implicit classes in scala).

implicit class ListOps[A](list: List[A]) {
  def getRandomElement: Option[A] = list match {
    case Nil => None
    case _ => list.lift(scala.util.Random.nextInt(list.size))
  }
  def randomChoice(n: Int): Option[List[A]] =
    (1 to n).toList.foldLeft(Option(List[A]()))((acc, e) => getRandomElement.flatMap(r => acc.map(a => a :+ r)))
}

Now if the implicit class is in scope, you can:

val randomElement: Option[String] = List("this", "is", "a", "list").getRandomElement

If you are sure that the option contains some value, you can use the get method.

randomElement.get // This will return a String (or a NotSuchElementExeption)

Nonetheless, pattern matching or getOrElse are recommended:

randomElement match {
  case None => ??? // This is what you do when a None is encounter (e.g. for empty lists)
  case Some(result) => ??? // The variable result contains a string. 

Note that the randomChoice method assumes substitution of elements.

Comments

1

A better answer that does not involve reshuffling the array at all would be this:

import scala.util.Random

object sample {
  //gets random element from array
  def arr[T](items:Array[T]):T = {
    items(Random.nextInt(items.length))
  }
}

This also works generically

1 Comment

Consider sample.arr[Int](Array[Int]())

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.