List Shuffle in Elm

By | 2016-06-16

Since I started working with Elm a couple weeks ago I’ve been impressed with the language as a whole and especially the tooling. Now I’m new to the functional programming arena and one of the areas that tripped me up besides the JSON decoding is randomness. Generating randomness in a functional language can be quite difficult. In this post we are going to take a look at an attempt to ‘randomly’ shuffle a list of objects. True randomness is an illusion when it comes to algorithms.

Generating a Random Number
In this article we are going to stick with just using the Repl. The only imports we’ll need are:

import Random
import List

To generate a random integer between 1 and 100 we can use

Random.step (Random.int 1 100) (Random.initialSeed 535235345) |> fst

The 535235345 is just a random number to start our seed. In a real application I use Time to get a unique number. Using the same seed will generate the same random number. Also, if we left off the |> fst or used |> snd we could capture the seed generated by that call and use it to generate another number.

Generating a List of Random Numbers
Now the general premise(according to the title of this article) is that we want to shuffle a list of objects. We are going to accomplish that by generating a list of random integers that is the same length as the list of objects we wish to shuffle. Then we will zip the two lists together, sort by the randomized integers, and then unzip the list to retrieve our original list of objects in their new order. This isn’t the ideal shuffle, but works in many scenarios. First let’s look at our sample list of objects we want to shuffle:

olist = [{ id = 'a' },{ id = 'b' },{ id = 'c' },{ id = 'd' },{ id = 'e' }]

Now our random list of integers using the length of our list above:

rlist = Random.step (Random.list (List.length olist) (Random.int 1 100)) (Random.initialSeed 655432425) |> fst

Zip the Two Lists Together
This will combine the two lists into a list of tuples, ie: [(78,{ id = 'a' }),(81,{ id = 'b' }),(45,{ id = 'c' }),(54,{ id = 'd' }),(82,{ id = 'e' })]

zipped = List.map2 (,) rlist olist

Sort
Sort by the first item in each tuple.

sorted = zipped |> List.sortBy fst

Unzip
Unzip the list of tuples back into two lists and retrieve the second list.

List.unzip zipped |> snd

Combine It All Together
I broke this out step by step so you can see the result of each step, but it could easily be combined into a single statement:

List.map2 (,) rlist olist |> List.sortBy fst |> List.unzip |> snd
Category: Elm