All Pairs in Elixir

I have a list of things. Numbers or people or filenames. Whatever.

I now want to pair all of those things together. They should be paired up in such a way that {1, 2} is a distinct pair from {2, 1}.

If we had a list of names ["ada", "dijkstra", "knuth"], then we'd like a list of pairs that looked something like this:

  {"ada", "dijkstra"}, {"ada", "knuth"},
  {"dijkstra", "ada"}, {"dijkstra", "knuth"},
  {"knuth", "ada"}, {"knuth", "dijkstra"}

With that in mind, let's use a couple functions from the Enum module to get this done.

defmodule AllPairs do  
  def from_list(list) do
    Enum.flat_map(list, fn(x) ->
                      fn(y) -> y != x end,
                      fn(y) -> {x, y} end)

The two functions we are taking advantage of are Enum.flat_map/2 and Enum.filter_map/3.

We need to turn each item into a list of its pairs, but we ultimately need a one-dimensional list of pairs. The flat_map function allows us to iterate over all the items and eventually flatten the final result into a list.

The mapping function is a filter_map that first removes the current item, which ensures we don't end up with {"dijkstra", "dijkstra"}, and then produces tuples of the primary and secondary items forming the pair.

Let's see it in action:

> AllPairs.from_list([1,2,3,4,5])
[{1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 1}, {2, 3}, {2, 4}, {2, 5}, {3, 1},
 {3, 2}, {3, 4}, {3, 5}, {4, 1}, {4, 2}, {4, 3}, {4, 5}, {5, 1}, {5, 2},
 {5, 3}, {5, 4}]

Fun stuff.

What about a function that produces all unique pairs from a list of items (i.e. {1,2} and {2,1} are the same thing)? I'll leave that as an exercise for the reader.