Utility functions for lists.
Summary
Functions
Chunks adjacent elements while a predicate holds for each neighbouring pair.
Returns elements from the first enumerable whose derived keys are absent from the second enumerable.
Returns elements from the first enumerable that have no comparator match in the second enumerable.
Returns the Cartesian product of two lists.
Returns all permutations of a list.
Returns every subsequence of a list.
Returns elements whose derived keys are present in only one enumerable.
Returns elements from both enumerables that have no comparator match in the other enumerable.
Returns all unique unordered pairs from a list.
Builds a map by indexing each element with a key function.
Returns all matching pairs from two enumerables.
Returns elements from the first enumerable whose derived keys are present in the second enumerable.
Returns elements from the first enumerable that have a comparator match in the second enumerable.
Alternates elements from two lists.
Maps a list while threading an accumulator from left to right.
Maps a list while threading an accumulator from right to left.
Moves the element at one index to another index.
Maps each element into one of two partitions.
Reduces elements into groups selected by a key function.
Splits a list before every element that satisfies a predicate.
Transposes a rectangular list of rows.
Returns unique elements according to a comparator.
Expands maps by replacing a list field with each of its values.
Functions
Chunks adjacent elements while a predicate holds for each neighbouring pair.
The predicate receives the previous element and the current element. A truthy result keeps the current element in the active chunk; a falsy result starts a new chunk.
Examples
iex> Algo.List.chunk_while_adjacent([1, 2, 3, 1, 2], fn left, right -> right == left + 1 end)
[[1, 2, 3], [1, 2]]
@spec difference_by(Enumerable.t(), Enumerable.t(), (any() -> any())) :: list()
Returns elements from the first enumerable whose derived keys are absent from the second enumerable.
Duplicate elements from the first enumerable are preserved when their key is absent from the second enumerable.
Examples
iex> Algo.List.difference_by([%{id: 1}, %{id: 2}], [%{id: 2}], & &1.id)
[%{id: 1}]
@spec difference_with(Enumerable.t(), Enumerable.t(), (any(), any() -> as_boolean(term()))) :: list()
Returns elements from the first enumerable that have no comparator match in the second enumerable.
The comparator receives {left_element, right_element} and a truthy return
value means the elements match.
Examples
iex> Algo.List.difference_with(["a", "bb"], ["c"], Algo.eq_by?(&String.length/1))
["bb"]
Returns the Cartesian product of two lists.
Pass :lists to return each pair as a two-item list, or :tuples to return
each pair as a tuple.
Examples
iex> Algo.List.get_cartesian_product([1, 2], [:a, :b], :tuples)
[{1, :a}, {1, :b}, {2, :a}, {2, :b}]
iex> Algo.List.get_cartesian_product([1, 2], [:a], :lists)
[[1, :a], [2, :a]]
Returns all permutations of a list.
Duplicate input values may produce duplicate permutations.
Examples
iex> Algo.List.get_permutations([1, 2, 3])
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
iex> Algo.List.get_permutations([])
[[]]
Returns every subsequence of a list.
The empty list is included. Subsequence order follows the recursive generation order used by the function.
Examples
iex> Algo.List.get_subsequences([1, 2])
[[], [1], [2], [1, 2]]
iex> Algo.List.get_subsequences([])
[[]]
@spec get_symmetric_difference_by(Enumerable.t(), Enumerable.t(), (any() -> any())) :: list()
Returns elements whose derived keys are present in only one enumerable.
Items from the first enumerable are returned before items from the second. Duplicates are preserved for keys that are exclusive to their side.
Examples
iex> Algo.List.get_symmetric_difference_by([%{id: 1}, %{id: 2}], [%{id: 2}, %{id: 3}], & &1.id)
[%{id: 1}, %{id: 3}]
@spec get_symmetric_difference_with(Enumerable.t(), Enumerable.t(), (any(), any() -> as_boolean( term() ))) :: list()
Returns elements from both enumerables that have no comparator match in the other enumerable.
Items from the first enumerable are returned before items from the second. For
first-side items, the comparator receives {left_element, right_element}; for
second-side items, it receives {right_element, left_element}.
Examples
iex> Algo.List.get_symmetric_difference_with(["a", "bb"], ["c", "ddd"], Algo.eq_by?(&String.length/1))
["bb", "ddd"]
Returns all unique unordered pairs from a list.
Pass :lists to return pairs as two-item lists, or :tuples to return pairs
as tuples.
Examples
iex> Algo.List.get_unique_pairs([1, 2, 3], :lists)
[[1, 2], [1, 3], [2, 3]]
iex> Algo.List.get_unique_pairs([1, 2], :tuples)
[{1, 2}]
iex> Algo.List.get_unique_pairs([1], :lists)
[]
@spec index_by(Enumerable.t(), (any() -> any())) :: map()
Builds a map by indexing each element with a key function.
If multiple elements produce the same key, the later element wins.
Examples
iex> Algo.List.index_by([%{id: 1, name: "old"}, %{id: 1, name: "new"}], & &1.id)
%{1 => %{id: 1, name: "new"}}
@spec inner_join(Enumerable.t(), Enumerable.t(), (any(), any() -> as_boolean(term()))) :: [ {any(), any()} ]
Returns all matching pairs from two enumerables.
The predicate receives a left element and a right element. Every truthy match
is emitted as {left_element, right_element}.
Examples
iex> Algo.List.inner_join([%{id: 1}], [%{user_id: 1}, %{user_id: 2}], fn left, right -> left.id == right.user_id end)
[{%{id: 1}, %{user_id: 1}}]
@spec intersect_by(Enumerable.t(), Enumerable.t(), (any() -> any())) :: list()
Returns elements from the first enumerable whose derived keys are present in the second enumerable.
Input order and duplicate elements from the first enumerable are preserved.
Examples
iex> Algo.List.intersect_by([%{id: 1}, %{id: 2}, %{id: 2}], [%{id: 2}], & &1.id)
[%{id: 2}, %{id: 2}]
@spec intersect_with(Enumerable.t(), Enumerable.t(), (any(), any() -> as_boolean(term()))) :: list()
Returns elements from the first enumerable that have a comparator match in the second enumerable.
The comparator receives {left_element, right_element} and a truthy return
value means the elements match.
Examples
iex> Algo.List.intersect_with(["a", "bb"], ["c"], Algo.eq_by?(&String.length/1))
["a"]
Alternates elements from two lists.
When one list is longer, the remaining elements from that list are appended to the result.
Examples
iex> Algo.List.interweave([1, 2, 3], [:a, :b])
[1, :a, 2, :b, 3]
iex> Algo.List.interweave([], [:a, :b])
[:a, :b]
Maps a list while threading an accumulator from left to right.
The function receives each element and the current accumulator, and must return
{mapped_element, next_accumulator}.
Examples
iex> Algo.List.map_accum([1, 2, 3], 0, fn x, sum -> {x * 2, sum + x} end)
{[2, 4, 6], 6}
Maps a list while threading an accumulator from right to left.
The function receives each element and the current accumulator, and must return
{mapped_element, next_accumulator}. The returned mapped list keeps the input
order.
Examples
iex> Algo.List.map_accum_right([1, 2, 3], 0, fn x, sum -> {x + sum, sum + x} end)
{[6, 5, 3], 6}
Moves the element at one index to another index.
Indexes are zero-based. Negative indexes count back from the end, like
Enum.at/2. Both indexes must point at existing elements; otherwise
ArgumentError is raised.
Examples
iex> Algo.List.move_at([:a, :b, :c, :d], 0, 2)
[:b, :c, :a, :d]
iex> Algo.List.move_at([:a, :b, :c], -1, 0)
[:c, :a, :b]
@spec partition_map(Enumerable.t(), (any() -> {:left, any()} | {:right, any()})) :: {list(), list()}
Maps each element into one of two partitions.
The mapping function must return {:left, value} or {:right, value}. The
result is {left_values, right_values} with input order preserved in each
partition.
Examples
iex> Algo.List.partition_map([1, 2, 3], fn x -> if rem(x, 2) == 0, do: {:right, x * 10}, else: {:left, x} end)
{[1, 3], [20]}
Reduces elements into groups selected by a key function.
Each new key starts with initial_acc. The reducer receives the element and
the current accumulator for that key.
Examples
iex> Algo.List.reduce_by(["a", "bb", "c"], &String.length/1, 0, fn _value, count -> count + 1 end)
%{1 => 2, 2 => 1}
@spec split_whenever(list(), (any() -> as_boolean(term()))) :: [list()]
Splits a list before every element that satisfies a predicate.
Matching elements are retained as the first element of their new chunk. Empty chunks are not emitted.
Examples
iex> Algo.List.split_whenever([1, 2, 3, 2, 4], &(&1 == 2))
[[1], [2, 3], [2, 4]]
iex> Algo.List.split_whenever([:a, :b], fn _ -> false end)
[[:a, :b]]
Transposes a rectangular list of rows.
All rows must be lists with the same length. An empty list of rows returns an
empty list. If rows are not of equal lengths, the transposition stops at the end
of the shortest row, matching the behaviour of Enum.zip.
Examples
iex> Algo.List.transpose([[1, 2, 3], [:a, :b, :c]])
[[1, :a], [2, :b], [3, :c]]
@spec unique_with(Enumerable.t(), (any(), any() -> as_boolean(term()))) :: list()
Returns unique elements according to a comparator.
The first occurrence is kept. The comparator receives an already kept element and the candidate element; a truthy return value means the two elements are considered the same.
Examples
iex> Algo.List.unique_with(["a", "bb", "c"], Algo.eq_by?(&String.length/1))
["a", "bb"]
@spec unwind(Enumerable.t(), any()) :: list()
Expands maps by replacing a list field with each of its values.
Items with a list field emit one item per field value. An empty list emits no items. Missing fields and non-list fields are left unchanged.
Examples
iex> Algo.List.unwind([%{id: 1, tags: [:a, :b]}, %{id: 2, tags: []}], :tags)
[%{id: 1, tags: :a}, %{id: 1, tags: :b}]
iex> Algo.List.unwind([%{id: 1}, %{id: 2, tags: :a}], :tags)
[%{id: 1}, %{id: 2, tags: :a}]