Current section
Files
Jump to
Current section
Files
lib/pairs.ex
defmodule Pairs do
# TODO:
# * Dializer
# * Sanity check, only one per list
@moduledoc ~S"""
`Pairs` currently only has methods to create pairs from two lists: `create/2` and `create/4`.
"""
@doc ~S"""
Creates a list of pairs from the `left` and `right` lists.
Pairs will be matched on equality of the keys after the elements were passed
through the `left_key_fun` and `right_key_fun` respecively.
## Examples
iex> create(["Ant", "Buffalo"], &String.upcase/1, ["ant"], &String.upcase/1)
[{"Buffalo", nil}, {"Ant", "ant"}]
iex> create(["a", "b", "c", "d"], &String.to_atom/1, [:d, :e, :a], &(&1))
[{"b", nil}, {"c", nil}, {"a", :a}, {"d", :d}, {nil, :e}]
"""
def create(left, left_key_fun, right, right_key_fun)
when is_list(left) and is_list(right)
and is_function(left_key_fun) and is_function(right_key_fun) do
left_grouped = Enum.group_by(left, left_key_fun)
right_grouped = Enum.group_by(right, right_key_fun)
divide(Map.keys(left_grouped), Map.keys(right_grouped))
|> distribute(left_grouped, right_grouped)
end
@doc ~S"""
Creates a list of pairs from the `left` and `right` lists.
Two elements from the list end up in the same pair if they are equal.
`Pairs.create/4` offers more flexibility by accepting functions to extract keys.
## Examples
iex> create(["Ant", "Buffalo"], ["ant"])
[{"Ant", nil}, {"Buffalo", nil}, {nil, "ant"}]
iex> create(["Ant", "Buffalo"], ["Ant"])
[{"Buffalo", nil}, {"Ant", "Ant"}]
"""
def create(left, right) when is_list(left) and is_list(right) do
create(left, &id/1, right, &id/1)
end
@doc false
def divide(left, right) when is_list(left) and is_list(right) do
left = MapSet.new(left)
right = MapSet.new(right)
in_both = MapSet.intersection(left, right)
left_only = MapSet.difference(left, right)
right_only = MapSet.difference(right, left)
[left_only, in_both, right_only]
|> Enum.map(&MapSet.to_list/1)
|> List.to_tuple
end
defp distribute({left_only, in_both, right_only}, left_grouped, right_grouped) do
make_pairs(left_only, left_grouped, %{}) ++
make_pairs(in_both, left_grouped, right_grouped) ++
make_pairs(right_only, %{}, right_grouped)
end
defp make_pairs(keys, left, right) do
Enum.map(keys, fn(key) -> {get_one(left, key), get_one(right, key)} end)
end
defp get_one(map, key) do
Map.get(map, key, []) |> List.first
end
defp id(a), do: a
end