lib/helpers/sort.ex

defmodule AbsintheUtils.Helpers.Sort do
  @moduledoc """
  Generic sorting utils.
  """

  @doc """
  Sorts an `unsorted_enumerable` based on `sorted_enumerable`.
  `unsorted_enumerable` cannot contain duplicates and
  can only contain elements from `sorted_enumerable`.

  Use mappers to specify getters for each element in the enumerable.

  ## Examples

      iex> Sort.sort_alike([:b, :c, :a], [:a, :b, :c])
      [:a, :b, :c]

  """
  def sort_alike(
        unsorted_enumerable,
        sorted_enumerable,
        unsorted_enumerable_mapper \\ & &1,
        sorted_enumerable_mapper \\ & &1
      ) do
    value_to_index =
      sorted_enumerable
      |> Enum.map(sorted_enumerable_mapper)
      |> map_enum_to_index()

    unsorted_enumerable
    |> Enum.sort_by(fn element ->
      Map.fetch!(
        value_to_index,
        unsorted_enumerable_mapper.(element)
      )
    end)
  end

  @doc """
  Given an enumerable, returns a map of each element
  to the position in the original enumerable (zero based).

  ## Examples

      iex> Sort.map_enum_to_index([:a, :b, :c])
      %{a: 0, b: 1, c: 2}
  """
  @spec map_enum_to_index(list(any)) :: map
  def map_enum_to_index(enumerable) do
    enumerable
    |> Enum.with_index()
    |> Map.new(fn {element, index} -> {element, index} end)
  end
end