View Source Toolbox.Utils.Map (toolbox v5.4.10)

A set of utility functions for maps.

Summary

Functions

Creates deep map with value on key represented by path. If path is empty, it returns given value.

Deeply updates left map by right map.

Removes key represented as path (list of keys) deeply in map if it exists.

Converts map from flat to nested map.

Follows the path in the (deep) map, defaulting to default if some of the objects on the path is missing.

Extracts all (json-like) paths in the map.

Subtracts map from another map, the leaf-paths from the second map are removed from the first.

Converts regular nested map to flat map.

Functions

Link to this function

deep_create(list, value)

View Source
@spec deep_create(path :: list(), value :: any()) :: map() | any()

Creates deep map with value on key represented by path. If path is empty, it returns given value.

Examples

iex> Toolbox.Utils.Map.deep_create([], :some_value)
:some_value

iex> Toolbox.Utils.Map.deep_create(["a", :b], :other_value)
%{"a" => %{:b => :other_value}}
@spec deep_merge(map(), map()) :: map()

Deeply updates left map by right map.

Existing keys from left map not contained in right map are unchanged. New keys from right map are added. If the key is present in both maps and both values are maps, maps are deeply merges. If the key is present in both maps and value of one of them is not a map, value of key in left map is replaced by value in right map.

Example

iex> Toolbox.Utils.Map.deep_merge(%{a: 1}, %{b: 2})
%{a: 1, b: 2}

iex> Toolbox.Utils.Map.deep_merge(%{a: %{b: 1}}, %{a: %{c: 3}})
%{a: %{b: 1, c: 3}}

iex> Toolbox.Utils.Map.deep_merge(%{a: %{b: %{c: 1}}}, %{a: %{b: %{d: 2}}})
%{a: %{b: %{c: 1, d: 2}}}

iex> Toolbox.Utils.Map.deep_merge(%{a: 1}, %{a: %{b: 2}})
%{a: %{b:  2}}

iex> Toolbox.Utils.Map.deep_merge(%{a: %{b: 1}}, %{a: 2})
%{a: 2}

iex> Toolbox.Utils.Map.deep_merge(%{a: 1}, %{a: 2})
%{a: 2}
@spec deep_remove(map(), list()) :: map()

Removes key represented as path (list of keys) deeply in map if it exists.

Example

iex> Toolbox.Utils.Map.deep_remove(%{a: 1, b: 2}, [])
%{a: 1, b: 2}

iex> Toolbox.Utils.Map.deep_remove(%{a: 1, b: 2}, [:a])
%{b: 2}

iex> Toolbox.Utils.Map.deep_remove(%{a: 1, b: 2}, [:a, :aa])
%{a: 1, b: 2}

iex> Toolbox.Utils.Map.deep_remove(%{a: %{aa: 1, ab: 2}, b: 2}, [:a, :aa])
%{a: %{ab: 2}, b: 2}

iex> Toolbox.Utils.Map.deep_remove(%{a: %{aa: 1, ab: 2}, b: 2}, [:a, :aa, :aaa])
%{a: %{aa: 1, ab: 2}, b: 2}

Converts map from flat to nested map.

The input map must be flat with array keys, which represent the path to the respective values. The corresponding nested map is then constructed.

Also see to_flat_map/1 which performs the reverse operation.

Examples

iex> Toolbox.Utils.Map.from_flat_map(%{
...>   ["contact", "name"] => "Jored",
...>   ["contact", "address", "city"] => "Lrno",
...>   ["active", "ui"] => true,
...>   ["active", "backend"] => false
...> })
%{
  "contact" => %{
    "name" => "Jored",
    "address" => %{
      "city" => "Lrno"
    }
  },
  "active" => %{
    "ui" => true,
    "backend" => false
  }
}

iex> Toolbox.Utils.Map.from_flat_map(%{
...>   [:very, :very, :very, :very, :very, :very, :deep] => "object",
...>   [:very, :very, :very, :very, :very, :very, :nested] => "map"
...> })
%{
  very: %{
    very: %{
      very: %{
        very: %{
          very: %{
            very: %{
              deep: "object",
              nested: "map"
            }
          }
        }
      }
    }
  }
}

iex> Toolbox.Utils.Map.from_flat_map(%{})
%{}

iex> Toolbox.Utils.Map.from_flat_map(Toolbox.Utils.Map.to_flat_map(%{
...>   "contact" => %{
...>     "name" => "Jonas",
...>     "address" => %{"city" => "Brnp"}},
...>     "favorites" => %{
...>       "food" => %{
...>         1 => ["cornflakes", "corn"],
...>         5 => ["pizza"],
...>         8 => ["pasta"]
...>       }
...>     }
...>   }
...> ))
%{
  "contact" => %{
    "name" => "Jonas",
    "address" => %{"city" => "Brnp"}},
    "favorites" => %{
      "food" => %{
        1 => ["cornflakes", "corn"],
        5 => ["pizza"],
        8 => ["pasta"]
    }
  }
}
Link to this function

get_path(map, path, default \\ nil)

View Source
@spec get_path(map(), [binary() | atom()], any()) :: any()

Follows the path in the (deep) map, defaulting to default if some of the objects on the path is missing.

Example

iex> object = %{"some" => %{"deep" => %{"object" => "data"}}}
...> Toolbox.Utils.Map.get_path(object, ["some", "deep", "object"])
"data"
iex> Toolbox.Utils.Map.get_path(object, ["non-existent"], "default")
"default"

Extracts all (json-like) paths in the map.

Given any artitrarily nested map, the function returns all paths the map contains.

The output is not sorted in a predictable way.

Examples

iex> Toolbox.Utils.Map.json_paths(%{
...>    "contact" => %{
...>      "name" => "Trest",
...>      "phone" => 605554171,
...>      "address" => %{"city" => "Brno"}
...>    },
...>    "active" => false
...> }) |> Enum.sort()
[
  ["active"],
  ["contact", "address", "city"],
  ["contact", "name"],
  ["contact", "phone"],
]

iex> Toolbox.Utils.Map.json_paths(%{"a" => %{1 => %{%{"cool" => "stuff"} => %{atom: :work_too}}}})
[
  ["a", 1, %{"cool" => "stuff"}, :atom]
]

iex> Toolbox.Utils.Map.json_paths(%{})
[]
Link to this function

subtract(map, to_delete)

View Source

Subtracts map from another map, the leaf-paths from the second map are removed from the first.

Examples

iex> Toolbox.Utils.Map.subtract(%{a: 1, b: 2}, %{a: true})
%{b: 2}

iex> Toolbox.Utils.Map.subtract(%{a: 1}, %{a: %{b: %{c: true}}})
%{a: 1}

iex> Toolbox.Utils.Map.subtract(
...>   %{a: %{b: %{c: "1"}, d: "2"}, e: "3"},
...>   %{a: %{b: %{c: true}}, e: true}
...> )
%{a: %{b: %{}, d: "2"}}

iex> Toolbox.Utils.Map.subtract(
...>   %{"a" => 1, "b" => %{"c" => true}},
...>   %{"b" => %{"c" => true}}
...> )
%{"a" => 1, "b" => %{}}

iex> Toolbox.Utils.Map.subtract(%{a: 1, b: 2}, %{c: 2, d: 3})
%{a: 1, b: 2}

iex> Toolbox.Utils.Map.subtract(%{a: %{b: 1}}, %{a: true})
%{}

Converts regular nested map to flat map.

The input map can be any map - arbitrarily nested, the output is a flat map where keys are arrays representing the path to the respective value.

Also see from_flat_map/1 which performs the reverse operation.

Examples

iex> Toolbox.Utils.Map.to_flat_map(%{
...>   "contact" => %{
...>     "name" => "Kert",
...>     "address" => %{"city" => "Krno"}
...>   },
...>   "status" => %{"ui" => %{"active" => true},
...>   "backend" => %{"fluent" => false}}
...> })
%{
  ["contact", "name"] => "Kert",
  ["contact", "address", "city"] => "Krno",
  ["status", "ui", "active"] => true,
  ["status", "backend", "fluent"] => false
}

iex> Toolbox.Utils.Map.to_flat_map(%{good: %{1 => :nice}, very: %{deep: %{%{ish: :map} => :askey}}})
%{
  [:good, 1] => :nice,
  [:very, :deep, %{ish: :map}] => :askey
}

iex> Toolbox.Utils.Map.to_flat_map(%{"regular" => "map"})
%{
  ["regular"] => "map"
}

iex> Toolbox.Utils.Map.to_flat_map(%{})
%{}