defmodule Algo do @moduledoc """ A collection of utility functions for working with lists, maps, keyword lists, and strings. This module is a convenience wrapper around the various `Algo` submodules, providing a single namespace for importing functions. """ @doc group: "Map and keyword list functions" defdelegate evolve(map, evolution_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate invert(map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate get_paths(map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate has_path?(map, path), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate get_path(map, path, default \\ nil), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate fetch_path(map, path), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate fetch_path!(map, path), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate put_path(map, path, value), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate put_new_path(map, path, value), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate replace_path(map, path, value), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate replace_path!(map, path, value), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate update_path(map, path, initial, fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate update_path!(map, path, fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate delete_path(map, path), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate get_values_at_paths(map, paths), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate path_satisfies?(map, path, fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate merge_right(left_map, right_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate merge_left(left_map, right_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate deep_merge_left(left_map, right_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate deep_merge_right(left_map, right_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate deep_merge_with(left_map, right_map, fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate get_depth(map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate each_depth_first(map, fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate each_breadth_first(map, fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate project(list_of_maps, keys), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate where_all?(map, condition_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate where_any?(map, condition_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate where_eq?(map, condition_map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate prop(name), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate props(names), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate rename_keys(map, renames), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate unnest_keys(map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate stringify_keys(map), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate atomise_keys_with(map, key_fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate atomize_keys_with(map, key_fun), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate equivalent?(map, kw_list), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate as_map(kw_list), to: Algo.Assoc @doc group: "Map and keyword list functions" defdelegate as_keyword_list(map), to: Algo.Assoc @doc group: "List functions" defdelegate interweave(list1, list2), to: Algo.List @doc group: "List functions" defdelegate get_cartesian_product(list1, list2, output_as), to: Algo.List @doc group: "List functions" defdelegate get_permutations(list), to: Algo.List @doc group: "List functions" defdelegate get_subsequences(list), to: Algo.List @doc group: "List functions" defdelegate get_unique_pairs(list, output_as), to: Algo.List @doc group: "List functions" defdelegate map_accum(list, acc, fun), to: Algo.List @doc group: "List functions" defdelegate map_accum_right(list, acc, fun), to: Algo.List @doc group: "List functions" defdelegate chunk_while_adjacent(list, fun), to: Algo.List @doc group: "List functions" defdelegate split_whenever(list, fun), to: Algo.List @doc group: "List functions" defdelegate transpose(rows), to: Algo.List @doc group: "List functions" defdelegate move_at(list, from_index, to_index), to: Algo.List @doc group: "List functions" defdelegate unique_with(enumerable, fun), to: Algo.List @doc group: "List functions" defdelegate index_by(enumerable, fun), to: Algo.List @doc group: "List functions" defdelegate reduce_by(enumerable, key_fun, initial_acc, reduce_fun), to: Algo.List @doc group: "List functions" defdelegate unwind(enumerable, field), to: Algo.List @doc group: "List functions" defdelegate partition_map(enumerable, fun), to: Algo.List @doc group: "List functions" defdelegate difference_by(left, right, key_fun), to: Algo.List @doc group: "List functions" defdelegate difference_with(left, right, fun), to: Algo.List @doc group: "List functions" defdelegate intersect_by(left, right, key_fun), to: Algo.List @doc group: "List functions" defdelegate intersect_with(left, right, fun), to: Algo.List @doc group: "List functions" defdelegate get_symmetric_difference_by(left, right, key_fun), to: Algo.List @doc group: "List functions" defdelegate get_symmetric_difference_with(left, right, fun), to: Algo.List @doc group: "List functions" defdelegate inner_join(left, right, fun), to: Algo.List @doc group: "String functions" defdelegate humanise(str), to: Algo.String @doc group: "String functions" defdelegate humanize(str), to: Algo.String, as: :humanise @doc """ Returns a comparator that checks equality after applying a function. ## Examples iex> same_length? = Algo.eq_by?(&String.length/1) iex> same_length?.("a", "b") true iex> same_length? = Algo.eq_by?(&String.length/1) iex> same_length?.("a", "bb") false iex> Algo.unique_with(["a", "bb", "c"], Algo.eq_by?(&String.length/1)) ["a", "bb"] """ @doc group: "Higher-order functions" @spec eq_by?((any -> any)) :: (any, any -> boolean) def eq_by?(fun) when is_function(fun, 1) do fn left, right -> fun.(left) == fun.(right) end end @doc """ Returns whether all elements are unique after applying a function. ## Examples iex> Algo.all_different_by?(["a", "bb", "ccc"], &String.length/1) true iex> Algo.all_different_by?(["a", "b"], &String.length/1) false """ @doc group: "Enumerable functions" @spec all_different_by?(Enumerable.t(), (any -> any)) :: boolean def all_different_by?(enumerable, fun) do Enum.count(Enum.uniq_by(enumerable, fun)) == Enum.count(enumerable) end @doc """ Returns whether every element in an enumerable is unique. ## Examples iex> Algo.all_different?([1, 2, 3]) true iex> Algo.all_different?([1, 2, 1]) false """ @doc group: "Enumerable functions" @spec all_different?(Enumerable.t()) :: boolean def all_different?(enumerable) do all_different_by?(enumerable, & &1) end defmodule Alt do @moduledoc """ Provides alternative non-verb names for functions in Algo. These better reflect the purity of functions and immutability of data structures. """ @doc """ Alias for `Algo.evolve/2`. """ defdelegate evolved(map, evolution_map), to: Algo, as: :evolve @doc """ Alias for `Algo.invert/1`. """ defdelegate inverted(map), to: Algo, as: :invert @doc """ Alias for `Algo.get_paths/1`. """ defdelegate paths(map), to: Algo, as: :get_paths @doc """ Alias for `Algo.has_path?/2`. """ defdelegate has_path?(map, path), to: Algo, as: :has_path? @doc """ Alias for `Algo.get_path/3`. """ defdelegate maybe_value_at_path(map, path, default \\ nil), to: Algo, as: :get_path @doc """ Alias for `Algo.fetch_path/2`. """ defdelegate value_at_path(map, path), to: Algo, as: :fetch_path @doc """ Alias for `Algo.fetch_path!/2`. """ defdelegate value_at_path!(map, path), to: Algo, as: :fetch_path! @doc """ Alias for `Algo.put_path/3`. """ defdelegate with_path(map, path, value), to: Algo, as: :put_path @doc """ Alias for `Algo.put_new_path/3`. """ defdelegate with_new_path(map, path, value), to: Algo, as: :put_new_path @doc """ Alias for `Algo.replace_path/3`. """ defdelegate with_replaced_path(map, path, value), to: Algo, as: :replace_path @doc """ Alias for `Algo.replace_path!/3`. """ defdelegate with_replaced_path!(map, path, value), to: Algo, as: :replace_path! @doc """ Alias for `Algo.update_path/4`. """ defdelegate with_updated_path(map, path, initial, fun), to: Algo, as: :update_path @doc """ Alias for `Algo.update_path!/3`. """ defdelegate with_updated_path!(map, path, fun), to: Algo, as: :update_path! @doc """ Alias for `Algo.delete_path/2`. """ defdelegate without_path(map, path), to: Algo, as: :delete_path @doc """ Alias for `Algo.get_values_at_paths/2`. """ defdelegate values_at_paths(map, paths), to: Algo, as: :get_values_at_paths @doc """ Alias for `Algo.path_satisfies?/3`. """ defdelegate path_satisfies?(map, path, fun), to: Algo, as: :path_satisfies? @doc """ Alias for `Algo.merge_right/2`. """ defdelegate right_merged(left_map, right_map), to: Algo, as: :merge_right @doc """ Alias for `Algo.merge_left/2`. """ defdelegate left_merged(left_map, right_map), to: Algo, as: :merge_left @doc """ Alias for `Algo.deep_merge_left/2`. """ defdelegate deep_left_merged(left_map, right_map), to: Algo, as: :deep_merge_left @doc """ Alias for `Algo.deep_merge_right/2`. """ defdelegate deep_right_merged(left_map, right_map), to: Algo, as: :deep_merge_right @doc """ Alias for `Algo.deep_merge_with/3`. """ defdelegate deep_merged_with(left_map, right_map, fun), to: Algo, as: :deep_merge_with @doc """ Alias for `Algo.get_depth/1`. """ defdelegate depth(map), to: Algo, as: :get_depth @doc """ Alias for `Algo.each_depth_first/2`. """ defdelegate each_depth_first(map, fun), to: Algo, as: :each_depth_first @doc """ Alias for `Algo.each_breadth_first/2`. """ defdelegate each_breadth_first(map, fun), to: Algo, as: :each_breadth_first @doc """ Alias for `Algo.project/2`. """ defdelegate projection(list_of_maps, keys), to: Algo, as: :project @doc """ Alias for `Algo.where_all?/2`. """ defdelegate where_all?(map, condition_map), to: Algo, as: :where_all? @doc """ Alias for `Algo.where_any?/2`. """ defdelegate where_any?(map, condition_map), to: Algo, as: :where_any? @doc """ Alias for `Algo.where_eq?/2`. """ defdelegate where_eq?(map, condition_map), to: Algo, as: :where_eq? @doc """ Alias for `Algo.interweave/2`. """ defdelegate interwoven(list1, list2), to: Algo, as: :interweave @doc """ Alias for `Algo.get_cartesian_product/3`. """ defdelegate cartesian_product(list1, list2, output_as), to: Algo, as: :get_cartesian_product @doc """ Alias for `Algo.get_permutations/1`. """ defdelegate permutations(list), to: Algo, as: :get_permutations @doc """ Alias for `Algo.get_subsequences/1`. """ defdelegate subsequences(list), to: Algo, as: :get_subsequences @doc """ Alias for `Algo.get_unique_pairs/2`. """ defdelegate unique_pairs(list, output_as), to: Algo, as: :get_unique_pairs @doc """ Alias for `Algo.map_accum/3`. """ defdelegate accumulated_map(list, acc, fun), to: Algo, as: :map_accum @doc """ Alias for `Algo.map_accum_right/3`. """ defdelegate right_accumulated_map(list, acc, fun), to: Algo, as: :map_accum_right @doc """ Alias for `Algo.chunk_while_adjacent/2`. """ defdelegate adjacent_chunks(list, fun), to: Algo, as: :chunk_while_adjacent @doc """ Alias for `Algo.split_whenever/2`. """ defdelegate split_whenever(list, fun), to: Algo, as: :split_whenever @doc """ Alias for `Algo.transpose/1`. """ defdelegate transposed(rows), to: Algo, as: :transpose @doc """ Alias for `Algo.move_at/3`. """ defdelegate with_moved_elem(list, from_index, to_index), to: Algo, as: :move_at @doc """ Alias for `Algo.unique_with/2`. """ defdelegate unique_with(enumerable, fun), to: Algo, as: :unique_with @doc """ Alias for `Algo.index_by/2`. """ defdelegate indexed_by(enumerable, fun), to: Algo, as: :index_by @doc """ Alias for `Algo.reduce_by/4`. """ defdelegate reduced_by(enumerable, key_fun, initial_acc, reduce_fun), to: Algo, as: :reduce_by @doc """ Alias for `Algo.unwind/2`. """ defdelegate unwound(enumerable, field), to: Algo, as: :unwind @doc """ Alias for `Algo.partition_map/2`. """ defdelegate partitioned_map(enumerable, fun), to: Algo, as: :partition_map @doc """ Alias for `Algo.difference_by/3`. """ defdelegate difference_by(left, right, key_fun), to: Algo, as: :difference_by @doc """ Alias for `Algo.difference_with/3`. """ defdelegate difference_with(left, right, fun), to: Algo, as: :difference_with @doc """ Alias for `Algo.intersect_by/3`. """ defdelegate intersection_by(left, right, key_fun), to: Algo, as: :intersect_by @doc """ Alias for `Algo.intersect_with/3`. """ defdelegate intersection_with(left, right, fun), to: Algo, as: :intersect_with @doc """ Alias for `Algo.get_symmetric_difference_by/3`. """ defdelegate symmetric_difference_by(left, right, key_fun), to: Algo, as: :get_symmetric_difference_by @doc """ Alias for `Algo.get_symmetric_difference_with/3`. """ defdelegate symmetric_difference_with(left, right, fun), to: Algo, as: :get_symmetric_difference_with @doc """ Alias for `Algo.inner_join/3`. """ defdelegate inner_join(left, right, fun), to: Algo, as: :inner_join @doc """ Alias for `Algo.all_different_by?/2`. """ defdelegate all_different_by?(enumerable, fun), to: Algo, as: :all_different_by? @doc """ Alias for `Algo.eq_by?/1`. """ defdelegate eq_by?(fun), to: Algo, as: :eq_by? @doc """ Alias for `Algo.all_different?/1`. """ defdelegate all_different?(enumerable), to: Algo, as: :all_different? @doc """ Alias for `Algo.prop/1`. """ defdelegate prop(name), to: Algo, as: :prop @doc """ Alias for `Algo.props/1`. """ defdelegate props(names), to: Algo, as: :props @doc """ Alias for `Algo.rename_keys/2`. """ defdelegate with_renamed_keys(map, renames), to: Algo, as: :rename_keys @doc """ Alias for `Algo.unnest_keys/1`. """ defdelegate with_unnested_keys(map), to: Algo, as: :unnest_keys @doc """ Alias for `Algo.stringify_keys/1`. """ defdelegate with_stringified_keys(map), to: Algo, as: :stringify_keys @doc """ Alias for `Algo.atomise_keys_with/2`. """ defdelegate with_atomised_keys(map, key_fun), to: Algo, as: :atomise_keys_with @doc """ Alias for `Algo.atomize_keys_with/2`. """ defdelegate with_atomized_keys(map, key_fun), to: Algo, as: :atomize_keys_with @doc """ Alias for `Algo.equivalent?/2`. """ defdelegate equivalent?(map, kw_list), to: Algo, as: :equivalent? @doc """ Alias for `Algo.as_map/1`. """ defdelegate as_map(kw_list), to: Algo, as: :as_map @doc """ Alias for `Algo.as_keyword_list/1`. """ defdelegate as_keyword_list(map), to: Algo, as: :as_keyword_list @doc """ Alias for `Algo.humanise/1`. """ defdelegate humanised(str), to: Algo, as: :humanise @doc """ Alias for `Algo.humanize/1`. """ defdelegate humanized(str), to: Algo, as: :humanize end end