Sheharyar
Sheharyar

Reputation: 75740

Get Random Element(s) from a List

I'm basically looking for an Elixir equivalent of Ruby's Array#sample. Something that would let me do this:

list = [1,2,3,4,5,6,7]

sample(list)
#=> 4

sample(list, 3)
#=> [6, 2, 5]

I didn't find anything in the Elixir List Docs either.

Upvotes: 37

Views: 20821

Answers (4)

José Valim
José Valim

Reputation: 51349

There is no such function in Elixir 1.0, so you need to implement it yourself as mentioned by the other solutions. However, Enum.random/1 is coming with Elixir v1.1: https://hexdocs.pm/elixir/Enum.html#random/1

Upvotes: 12

Sheharyar
Sheharyar

Reputation: 75740

Updated Answer

As José Valim said in his answer, in Elixir 1.1 and above, you can now use these methods to get random element(s) from a list:

Example:

Enum.random(list)                         #=> 4

Enum.take_random(list, 3)                 #=> [3, 9, 1]
Enum.take_random(list, 1)                 #=> [7]

Remember to call :random.seed(:erlang.now) first!


Original Answer

I'm still unable to find a 'proper' and 'magical' way to do this, but this is the best I could come up:

For getting a single random element:

list |> Enum.shuffle |> hd
#=> 4

Note: This gives an exception if the list is empty

For getting multiple random elements:

list |> Enum.shuffle |> Enum.take(3)
#=> [7, 1, 5]

Upvotes: 66

Ilija Eftimov
Ilija Eftimov

Reputation: 820

If you are running a newer version of Elixir, there's the Enum.take_random/2 function that was added in Elixir v1.1.

From Elixir's v1.1 documentation:

Takes random items from a collection.

Notice this function will traverse the whole collection to get the random sublist of collection. If you want the random number between two integers, the best option is to use the :random module.

So, answering the question, taking multiple items from the list would look like:

list = [1,2,3,4,5,6,7]
Enum.take_random(list, 3)
#=> [2, 4, 1]

Or, for a single item:

list = [1,2,3,4,5,6,7]
Enum.take_random(list, 1)
#=> [1]

Upvotes: 1

Simon Woolf
Simon Woolf

Reputation: 713

Another way (for sampling one element only) is:

list |> Enum.at(:random.uniform(length(list)) - 1)

Possibly preferable over the other method if you're working with large enough lists that shuffling the whole list might have a performance impact.

Gazler's comment about using :random.seed(:erlang.now) still applies.

Upvotes: 5

Related Questions