Skip to content

Instantly share code, notes, and snippets.

@rubencaro
Created May 27, 2017 20:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rubencaro/b920080c17d9efc7efa9d376bc172a36 to your computer and use it in GitHub Desktop.
Save rubencaro/b920080c17d9efc7efa9d376bc172a36 to your computer and use it in GitHub Desktop.
Ursula's explosion of keys
defmodule Ursula.Combinations do
@keys ~w(a b c d e)
@doc """
Returns any combination of the elements in `enum` with exactly `k` elements.
Repeated elements are handled intelligently.
## Examples
iex> combinations([1, 2, 3], 2) |> Enum.to_list
[[1, 2], [1, 3], [2, 3]]
iex> combinations([1, 1, 2], 2) |> Enum.to_list
[[1, 1], [1, 2]]
"""
def combinations(enum, k) do
List.last(do_combinations(enum, k))
|> Enum.uniq
end
defp do_combinations(enum, k) do
combinations_by_length = [[[]]|List.duplicate([], k)]
list = Enum.to_list(enum)
List.foldr list, combinations_by_length, fn x, next ->
sub = :lists.droplast(next)
step = [[]|(for l <- sub, do: (for s <- l, do: [x|s]))]
:lists.zipwith(&:lists.append/2, step, next)
end
end
@doc """
Returns all possible combinations of Charlie keys
"""
def explode_keys do
1..Enum.count(@keys)
|> Enum.flat_map(&combinations(@keys, &1))
end
def keys, do: @keys
end
defmodule Ursula do
@moduledoc """
Documentation for Ursula.
"""
@exploded_keys Ursula.Combinations.explode_keys()
@doc """
Returns all possible combinations of keys for the Charlie
"""
def explode(ch) do
@exploded_keys
|> Enum.map(&get_join(ch, &1))
end
@doc """
Gets values for given `keys` from given `data` and joins them with given
separator, trying not to repeat itself.
"""
def get_join(data, keys), do: get_join(data, keys, [])
def get_join(data, keys, sep) when is_binary(sep),
do: get_join(data, keys, sep: sep)
def get_join(data, keys, opts) do
opts = Keyword.merge([sep: "_", trim: false], opts)
keys
|> Enum.map(&(data[&1])) # get values
|> map_if(opts[:trim], &(&1 |> to_string |> String.trim))
|> Enum.reject(&(&1 == "" or &1 == nil)) # reject empty ones
|> Enum.join(opts[:sep])
end
@doc """
Applies given mapping function to given enumerable
only if given expression is `true`.
"""
def map_if(data, expression, map_fun) when is_function(expression) do
map_if(data, expression.(), map_fun)
end
def map_if(data, expression, map_fun) do
case expression do
true -> Enum.map(data, &(map_fun.(&1)))
_ -> data
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment