lib/bureaucrat/util.ex

defmodule Bureaucrat.Util do
  @moduledoc """
  Some functions used across Bureaucrat internally
  """

  @doc """
  Takes a list of pre-sorted entries and groups them by given function,
  but returns a list of tuples `[{k, v}, ...]` instead of a Map to preserve
  key sort order, which can be given to Enum functions identically.

  Implementation inspired by `Enum.group_by/3`
    -> https://github.com/elixir-lang/elixir/blob/v1.4.2/lib/elixir/lib/enum.ex#L1036
  """
  def stable_group_by(enumerable, key_fun, value_fun \\ fn x -> x end)
      when is_function(key_fun, 1) and is_function(value_fun, 1) do
    Enum.reduce(Enum.reverse(enumerable), [], fn entry, categories ->
      key = key_fun.(entry)
      value = value_fun.(entry)

      case categories do
        # key matches previous key -> add value to previous list
        [{^key, values} | tail] ->
          [{key, [value | values]} | tail]

        # otherwise -> start a new list with this value
        _ ->
          [{key, [value]} | categories]
      end
    end)
  end
end