# Generated by erl2ex (http://github.com/dazuma/erl2ex) # From Erlang source: /home/dgulino/Documents/workspace/nested/src/nested.erl # At: 2022-06-25 17:24:15 defmodule Nested do @moduledoc """ Library to work with nested maps Elixir reimplementation of Erlang library [nested](https://github.com/odo/nested) """ @doc ~S""" Appends value to List at key path. iex> Nested.append(%{"test" => "rest", "rest" => [1]},["rest"],2) %{"rest" => [1, 2], "test" => "rest"} """ @spec append(map :: map(), path :: [any()], value :: any()) :: map() def append(map, path, value) do appendFun = fn list when is_list(list) -> list ++ [value] _ -> :erlang.error(:no_list) end update!(map, path, appendFun) end @doc ~S""" Appends value to List at key path. If key not found at path, add key and append value to default List provided Similar to Python's defaultdict iex> Nested.append(%{"test" => "rest", "rest" => [1]},["xest"],2,[]) %{"rest" => [1], "test" => "rest", "xest" => [2]} iex> Nested.append(%{"test" => "rest", "rest" => [1]},["rest"],2,[]) %{"rest" => [1, 2], "test" => "rest"} """ @spec append(map(), [any()], any(), list()) :: map() def append(map, path, value, default) do appendFun = fn list when is_list(list) -> list ++ [value] _ -> default ++ [value] end case has_key?(map,path) do true -> update!(map, path, appendFun) false -> put(map, path, default ++ [value]) end end @spec delete(map :: map(), list()) :: map() def delete(map, []) do map end @spec delete(map :: map(), list()) :: map() def delete(map, [lastKey]) do Map.delete(map,lastKey) end @spec delete(map :: map(), list()) :: map() def delete(map, [key | pathRest]) do case(Map.has_key?(map,key)) do true -> Map.put(map, key, delete(Map.get(map, key), pathRest)) false -> map end end @spec fetch!(map :: map(), [any()]) :: {:ok, map()} def fetch!(map, path) do case get(map, path) do :nil -> raise KeyError, "key path #{inspect(path)} not found" value -> {:ok, value} end end @spec fetch(map :: map(), [any()]) :: {:ok, map()} def fetch(map, path) do case get(map, path) do :nil -> :error value -> {:ok, value} end end @doc ~S""" ## Examples iex> Nested.get(%{test: :rest}, [:test] ) :rest iex> %{test: :rest} |> Nested.get([:test]) :rest iex> Nested.get(%{}, [:a]) nil iex> Nested.get(%{a: %{b: 1}}, [:a,:b]) 1 iex> Nested.get(%{a: %{b: 1}}, [:a,:c]) nil iex> Nested.get(%{a: %{b: 1}}, [:c], 3) 3 """ @spec get(map :: map(), [any()]) :: any() def get(map,[key | pathRest]) do get(Map.get(map, key), pathRest) end @spec get(map :: map(), []) :: any() def get(value, []) do value end @spec get(map :: map(), [any()], any()) :: any() def get(map, [key | pathRest], default) do case(Map.get(map,key,{__MODULE__, default})) do {__MODULE__, ^default} -> default nestedMap -> get(nestedMap, pathRest, default) end end @spec get(map :: map(), [], any()) :: any() def get(value, [], _) do value end @spec has_key?(map :: map(), list()) :: boolean() def has_key?(map, [key]) do Map.has_key?(map, key) end @spec has_key?(map :: map(), list()) :: boolean() def has_key?(map,[key | pathRest]) do case(map) do %{^key => subMap} -> has_key?(subMap, pathRest) _ -> false end end @spec keys(map :: map(), [any()]) :: [any()] def keys(map, [key | pathRest]) do keys(Map.get(map,key), pathRest) end @spec keys(map :: map(), []) :: [any()] def keys(map, []) do Map.keys(map) end @spec put(map :: map(), [any()], any()) :: map() def put(map, [key | pathRest], value) do subMap = case(Map.has_key?(map, key) and is_map(Map.get(map,key))) do true -> Map.get(map, key) false -> %{} end Map.put(map, key, put(subMap, pathRest, value) ) end @spec put(map :: map(), [], any()) :: map() def put(_, [], value) do value end @spec update!(map(), [any()], any()) :: map() def update!(map, path, valueOrFun) do try do updatef_internal!(map, path, valueOrFun) catch :error, {:error, {:no_map, pathRest, element}} -> pathLength = length(path) - length(pathRest) pathToThrow = :lists.sublist(path, pathLength) :erlang.error({:no_map, pathToThrow, element}) end end @spec updatef_internal!(map :: map(), [any()], function() | term()) :: map() defp updatef_internal!(map, [key | pathRest], valueOrFun) when is_map(map) do Map.put(map,key, updatef_internal!(Map.fetch!(map,key), pathRest, valueOrFun)) end @spec updatef_internal!(map :: map(), [], function() ) :: map() defp updatef_internal!(oldValue, [], fun) when is_function(fun) do fun.(oldValue) end @spec updatef_internal!(any(), [], any() ) :: map() defp updatef_internal!(_, [], value) do value end @spec updatef_internal!(any(), [any()], any() ) :: map() defp updatef_internal!(element, path, _) do :erlang.error({:error, {:no_map, path, element}}) end @spec update(map :: map(), path :: [any()], default :: any(), any() ) :: map() def update(map, path, default, valueOrFun) do try do updatef_internal(map, path, default, valueOrFun) catch :error, {:error, {:no_map, pathRest, element}} -> pathLength = length(path) - length(pathRest) pathToThrow = :lists.sublist(path, pathLength) :erlang.error({:no_map, pathToThrow, element}) end end @spec updatef_internal(map :: map(), path :: [any()], default :: any(), any() ) :: map() defp updatef_internal(map, [key | pathRest], default, valueOrFun) when is_map(map) do Map.put(map,key, updatef_internal(Map.get(map,key,default), pathRest, valueOrFun)) end @spec updatef_internal(map :: map(), path :: [], default :: any(), function() ) :: map() defp updatef_internal(oldValue, [], fun) when is_function(fun) do fun.(oldValue) end @spec updatef_internal(any(), [], any() ) :: map() defp updatef_internal(_, [], value) do value end @spec updatef_internal(any(), [any()], any() ) :: map() defp updatef_internal(element, path, _) do :erlang.error({:error, {:no_map, path, element}}) end end