A.RBMap (Aja v0.4.3) View Source

A Red-Black tree implementation of a map. It keeps keys sorted in ascending order.

Erlang's :gb_trees offer similar functionalities and performance. However A.RBMap:

Examples

A.RBMap offers the same API as Map :

iex> rb_map = A.RBMap.new([b: "Bat", a: "Ant", c: "Cat"])
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
iex> A.RBMap.get(rb_map, :c)
"Cat"
iex> A.RBMap.put(rb_map, :d, "Dinosaur")
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat", d: "Dinosaur"}>
iex> A.RBMap.delete(rb_map, :b)
#A.RBMap<%{a: "Ant", c: "Cat"}>
iex> Enum.to_list(rb_map)
[a: "Ant", b: "Bat", c: "Cat"]
iex> [c: "Cat", b: "Buffalo"] |> Enum.into(A.RBMap.new([a: "Ant", b: "Bat", d: "Dinosaur"]))
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat", d: "Dinosaur"}>

Tree-specific functions

Due to its sorted nature, A.RBMap also offers some extra methods not present in Map, like:

  • first/1 and last/1 to efficiently retrieve the first (smallest) / last (largest) key-value pair
  • pop_first/1 and pop_last/1 to efficiently pop the first (smallest) / last (largest) key-value pair
  • foldl/3 and foldr/3 to efficiently fold (reduce) from left-to-right or right-to-left

Examples:

iex> rb_map = A.RBMap.new(%{1 => "一", 2 => "二", 3 => "三"})
iex> A.RBMap.first(rb_map)
{1, "一"}
iex> {3, "三", updated} = A.RBMap.pop_last(rb_map)
iex> updated
#A.RBMap<%{1 => "一", 2 => "二"}>
iex> A.RBMap.foldr(rb_map, [], fn _key, value, acc -> [value | acc] end)
["一", "二", "三"]

Access behaviour

A.RBMap implements the Access behaviour.

iex> rb_map = A.RBMap.new([b: "Bat", a: "Ant", c: "Cat"])
iex> rb_map[:a]
"Ant"
iex> put_in(rb_map[:b], "Buffalo")
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> put_in(rb_map[:d], "Dinosaur")
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat", d: "Dinosaur"}>
iex> {"Cat", updated} = pop_in(rb_map[:c])
iex> updated
#A.RBMap<%{a: "Ant", b: "Bat"}>

With Jason

iex> A.RBMap.new(%{1 => "一", 2 => "二", 11 => "十一"}) |> Jason.encode!()
"{\"1\":\"\",\"2\":\"\",\"11\":\"十一\"}"

It also preserves the key order.

Limitations: pattern-matching and equality

Like :gb_trees, A.RBMaps face two strong limitations:

  • pattern-matching on key-values like maps is NOT POSSIBLE
  • comparisons based on ==/2, ===/2 or the pin operator ^ are UNRELIABLE

In Elixir, pattern-matching and equality for structs work based on their internal representation. While this is a pragmatic design choice that simplifies the language, it means that we cannot rededine how they work for custom data structures.

Tree-based maps that are semantically equal (same key-value pairs in the same order) might be considered non-equal when comparing their internals, because there is not a unique way of representing one same map.

A.RBMap.equal?/2 should be used instead:

iex> rb_map1 = A.RBMap.new([a: "Ant", b: "Bat"])
#A.RBMap<%{a: "Ant", b: "Bat"}>
iex> rb_map2 = A.RBMap.new([b: "Bat", a: "Ant"])
#A.RBMap<%{a: "Ant", b: "Bat"}>
iex> rb_map1 == rb_map2
false
iex> A.RBMap.equal?(rb_map1, rb_map2)
true
iex> match?(^rb_map1, rb_map2)
false

An A.RBMap is represented internally using the %A.RBMap{} struct. This struct can be used whenever there's a need to pattern match on something being an A.RBMap:

iex> match?(%A.RBMap{}, A.RBMap.new(a: "Ant"))
true

Note, however, than A.RBMap is an opaque type: its struct internal fields must not be accessed directly.

Note about numbers

Unlike regular maps, A.RBMaps only uses ordering for key comparisons, not strict comparisons. Integers and floats are indistiguinshable as keys.

iex> %{1 => "一", 2 => "二"} |> Map.fetch(2.0)
:error
iex> A.RBMap.new(%{1 => "一", 2 => "二"}) |> A.RBMap.fetch(2.0)
{:ok, "二"}

Erlang's :gb_trees module works the same.

Difference with A.OrdMap

  • A.OrdMap keeps track of key insertion order
  • A.RBMap keeps keys sorted in ascending order whatever the insertion order is

Memory overhead

A.RBMap takes roughly 1.4x more memory than a regular map depending on the type of data:

iex> key_values = Enum.map(1..100, fn i -> {i, <<i>>} end)
iex> map_size = Map.new(key_values) |> :erts_debug.size()
658
iex> rb_map_size = A.RBMap.new(key_values) |> :erts_debug.size()
910
iex> :gb_trees.from_orddict(key_values) |> :erts_debug.size()
803
iex> div(100 * rb_map_size, map_size)
138

Underlying Red-Black Tree implementation

The underlying red-black tree implementation is available in A.RBTree.Map and is used in other modules such as A.OrdMap as well. The algorithm detail is described in its documentation.

Link to this section Summary

Functions

Deletes the entry in rb_map for a specific key.

Drops the given keys from rb_map.

Checks if two maps are equal.

Fetches the value for a specific key and returns it in a ok-tuple. If the key does not exist, returns :error.

Fetches the value for a specific key and returns it in a ok-tuple. If the key does not exist, returns :error.

Finds the {key, value} pair corresponding to the smallest key in rb_map. Returns nil for empty maps.

Folds (reduces) the given rb_map from the left with the function fun. Requires an accumulator acc.

Folds (reduces) the given rb_map from the right with the function fun. Requires an accumulator acc.

Converts a struct to a A.RBMap.

Gets the value for a specific key in rb_map.

Gets the value from key and updates it, all in one pass.

Gets the value from key and updates it, all in one pass.

Gets the value for a specific key in rb_map.

Returns whether the given key exists in rb_map.

Returns all keys from rb_map.

Finds the {key, value} pair corresponding to the largest key in rb_map. Returns nil for empty maps.

Merges two maps into one.

Returns a new empty map.

Creates a map from an enumerable.

Creates a map from an enumerable via the given transform function.

Returns the value for key and the updated map without key.

Returns the value for key and the updated map without key.

Finds and pops the {key, value} pair corresponding to the smallest key in rb_map.

Finds and pops the {key, value} pair corresponding to the largest key in rb_map.

Lazily returns and removes the value associated with key in rb_map.

Puts the given value under key in rb_map.

Puts the given value under key unless the entry key already exists in rb_map.

Evaluates fun and puts the result under key in rb_map unless key is already present.

Puts a value under key only if the key already exists in rb_map.

Puts a value under key only if the key already exists in rb_map.

Returns the number of keys in rb_map.

Returns a new map with all the key-value pairs in rb_map where the key is in keys.

Returns all values from rb_map.

Updates the key in rb_map with the given function.

Puts a value under key only if the key already exists in rb_map.

Returns all values from rb_map.

Link to this section Types

Link to this opaque

iterator(key, value)

View Source (opaque)

Specs

iterator(key, value)

Specs

key() :: term()

Specs

t()

Specs

t(key, value)

Specs

value() :: term()

Link to this section Functions

Specs

delete(t(k, v), k) :: t(k, v) when k: key(), v: value()

Deletes the entry in rb_map for a specific key.

If the key does not exist, returns rb_map unchanged.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.delete(rb_map, :b)
#A.RBMap<%{a: "Ant", c: "Cat"}>
iex> A.RBMap.delete(rb_map, :z)
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>

Specs

drop(t(k, v), [k]) :: t(k, v) when k: key(), v: value()

Drops the given keys from rb_map.

If keys contains keys that are not in rb_map, they're simply ignored.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.drop(rb_map, [:b, :d])
#A.RBMap<%{a: "Ant", c: "Cat"}>
Link to this function

equal?(rb_map1, rb_map2)

View Source

Specs

equal?(t(), t()) :: boolean()

Checks if two maps are equal.

The comparison between keys is done using ==/2, the comparison between values with strict equality ===/2.

Examples

iex> A.RBMap.equal?(A.RBMap.new(a: 1, b: 2), A.RBMap.new(b: 2, a: 1))
true
iex> A.RBMap.equal?(A.RBMap.new([{1, "一"}, {2, "二"}]), A.RBMap.new([{1, "一"}, {2, "二"}]))
true
iex> A.RBMap.equal?(A.RBMap.new(a: 1, b: 2), A.RBMap.new(a: 3, b: 2))
false
iex> A.RBMap.equal?(A.RBMap.new(a: 1, b: 2), A.RBMap.new(a: 1.0, b: 2.0))
false

Specs

fetch(t(k, v), k) :: {:ok, v} | :error when k: key(), v: value()

Fetches the value for a specific key and returns it in a ok-tuple. If the key does not exist, returns :error.

Examples

iex> rb_map = A.RBMap.new(a: "A", b: "B", c: "C")
iex> A.RBMap.fetch(rb_map, :c)
{:ok, "C"}
iex> A.RBMap.fetch(rb_map, :z)
:error

Specs

fetch!(t(k, v), k) :: v when k: key(), v: value()

Fetches the value for a specific key and returns it in a ok-tuple. If the key does not exist, returns :error.

Examples

iex> rb_map = A.RBMap.new(a: "A", b: "B", c: "C")
iex> A.RBMap.fetch!(rb_map, :c)
"C"
iex> A.RBMap.fetch!(rb_map, :z)
** (KeyError) key :z not found in: #A.RBMap<%{a: "A", b: "B", c: "C"}>
Link to this function

first(rb_map, default \\ nil)

View Source

Specs

first(t(k, v), default) :: {k, v} | default
when k: key(), v: value(), default: term()

Finds the {key, value} pair corresponding to the smallest key in rb_map. Returns nil for empty maps.

This is very efficient and can be done in O(log(n)). It should be preferred over Enum.min/3.

Examples

iex> A.RBMap.new([b: "B", d: "D", a: "A", c: "C"]) |> A.RBMap.first()
{:a, "A"}
iex> A.RBMap.new([]) |> A.RBMap.first()
nil
iex> A.RBMap.new([]) |> A.RBMap.first(:error)
:error

Folds (reduces) the given rb_map from the left with the function fun. Requires an accumulator acc.

Examples

iex> rb_map = A.RBMap.new([b: 22, a: 11, c: 33])
iex> A.RBMap.foldl(rb_map, 0, fn _key, value, acc -> value + acc end)
66
iex> A.RBMap.foldl(rb_map, [], fn key, value, acc -> [{key, value * 2} | acc] end)
[c: 66, b: 44, a: 22]

Folds (reduces) the given rb_map from the right with the function fun. Requires an accumulator acc.

Unlike linked lists, this is as efficient as foldl/3. This can typically save a call to Enum.reverse/1 on the result when building a list.

Examples

iex> rb_map = A.RBMap.new([b: 22, a: 11, c: 33])
iex> A.RBMap.foldr(rb_map, 0, fn _key, value, acc -> value + acc end)
66
iex> A.RBMap.foldr(rb_map, [], fn key, value, acc -> [{key, value * 2} | acc] end)
[a: 22, b: 44, c: 66]

Specs

from_struct(atom() | struct()) :: t()

Converts a struct to a A.RBMap.

It accepts the struct module or a struct itself and simply removes the __struct__ field from the given struct or from a new struct generated from the given module.

Example

defmodule User do
  defstruct [:name, :age]
end

A.RBMap.from_struct(User)
#A.RBMap<%{age: nil, name: nil}>

A.RBMap.from_struct(%User{name: "john", age: 44})
#A.RBMap<%{name: "john"}>
Link to this function

get(rb_map, key, default \\ nil)

View Source

Specs

get(t(k, v), k, v) :: v | nil when k: key(), v: value()

Gets the value for a specific key in rb_map.

If key is present in rb_map then its value value is returned. Otherwise, default is returned.

If default is not provided, nil is used.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.get(rb_map, :a)
"Ant"
iex> A.RBMap.get(rb_map, :z)
nil
iex> A.RBMap.get(rb_map, :z, "Zebra")
"Zebra"
Link to this function

get_and_update(rb_map, key, fun)

View Source

Specs

get_and_update(t(k, v), k, (v -> {returned, v} | :pop)) :: {returned, t(k, v)}
when k: key(), v: value(), returned: term()

Gets the value from key and updates it, all in one pass.

Mirrors Map.get_and_update/3, see its documentation.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> {"bat", updated} = A.RBMap.get_and_update(rb_map, :b, fn current_value ->
...>   {current_value && String.downcase(current_value), "Buffalo"}
...> end)
iex> updated
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> {nil, updated} = A.RBMap.get_and_update(rb_map, :z, fn current_value ->
...>   {current_value && String.downcase(current_value), "Zebra"}
...> end)
iex> updated
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat", z: "Zebra"}>
iex> {"Bat", updated} = A.RBMap.get_and_update(rb_map, :b, fn _ -> :pop end)
iex> updated
#A.RBMap<%{a: "Ant", c: "Cat"}>
iex> {nil, updated} = A.RBMap.get_and_update(rb_map, :z, fn _ -> :pop end)
iex> updated
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
Link to this function

get_and_update!(rb_map, key, fun)

View Source

Specs

get_and_update!(t(k, v), k, (v -> {returned, v} | :pop)) :: {returned, t(k, v)}
when k: key(), v: value(), returned: term()

Gets the value from key and updates it, all in one pass.

Mirrors Map.get_and_update!/3, see its documentation.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> {"bat", updated} = A.RBMap.get_and_update!(rb_map, :b, fn current_value ->
...>   {current_value && String.downcase(current_value), "Buffalo"}
...> end)
iex> updated
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> A.RBMap.get_and_update!(rb_map, :z, fn current_value ->
...>   {current_value && String.downcase(current_value), "Zebra"}
...> end)
** (KeyError) key :z not found in: #A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
Link to this function

get_lazy(rb_map, key, fun)

View Source

Specs

get_lazy(t(k, v), k, v) :: v | nil when k: key(), v: value()

Gets the value for a specific key in rb_map.

If key is present in rb_map then its value value is returned. Otherwise, fun is evaluated and its result is returned.

This is useful if the default value is very expensive to calculate or generally difficult to setup and teardown again.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> expensive_fun = fn -> "Zebra" end
iex> A.RBMap.get_lazy(rb_map, :a, expensive_fun)
"Ant"
iex> A.RBMap.get_lazy(rb_map, :z, expensive_fun)
"Zebra"

Specs

has_key?(t(k, value()), k) :: boolean() when k: key()

Returns whether the given key exists in rb_map.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.has_key?(rb_map, :a)
true
iex> A.RBMap.has_key?(rb_map, :d)
false
iex> A.RBMap.has_key?(A.RBMap.new(%{1.0 => "uno"}), 1)
true

Specs

iterator(t(k, v)) :: iterator(k, v) when k: key(), v: value()

Specs

keys(t(k, value())) :: [k] when k: key()

Returns all keys from rb_map.

Examples

iex> rb_map = A.RBMap.new(b: "Bat", c: "Cat", a: "Ant")
iex> A.RBMap.keys(rb_map)
[:a, :b, :c]
Link to this function

last(rb_map, default \\ nil)

View Source

Specs

last(t(k, v), default) :: {k, v} | default
when k: key(), v: value(), default: term()

Finds the {key, value} pair corresponding to the largest key in rb_map. Returns nil for empty maps.

This is very efficient and can be done in O(log(n)). It should be preferred over Enum.max/3.

Examples

iex> A.RBMap.new([b: "B", d: "D", a: "A", c: "C"]) |> A.RBMap.last()
{:d, "D"}
iex> A.RBMap.new([]) |> A.RBMap.last()
nil
iex> A.RBMap.new([]) |> A.RBMap.last(:error)
:error

Specs

merge(t(k, v), t(k, v)) :: t(k, v) when k: key(), v: value()

Merges two maps into one.

All keys in rb_map2 will be added to rb_map1, overriding any existing one (i.e., the keys in rb_map2 "have precedence" over the ones in rb_map1).

Examples

iex> A.RBMap.merge(A.RBMap.new(%{a: 1, b: 2}), A.RBMap.new(%{a: 3, d: 4}))
#A.RBMap<%{a: 3, b: 2, d: 4}>

Specs

new() :: t()

Returns a new empty map.

Examples

iex> A.RBMap.new()
#A.RBMap<%{}>

Specs

new(Enumerable.t()) :: t()

Creates a map from an enumerable.

Keys are sorted upon insertion, and duplicated keys are removed; the latest one prevails.

Examples

iex> A.RBMap.new(b: "Bat", a: "Ant", c: "Cat")
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
iex> A.RBMap.new(b: "Bat", a: "Ant", b: "Buffalo", a: "Antelope")
#A.RBMap<%{a: "Antelope", b: "Buffalo"}>
Link to this function

new(enumerable, transform)

View Source

Specs

new(Enumerable.t(), (term() -> {k, v})) :: t(k, v) when k: key(), v: value()

Creates a map from an enumerable via the given transform function.

Duplicated keys are removed; the latest one prevails.

Examples

iex>  A.RBMap.new([:a, :b], fn x -> {x, x} end)
#A.RBMap<%{a: :a, b: :b}>

Specs

next(iterator(k, v)) :: {k, v, iterator(k, v)} | nil when k: key(), v: value()

See A.RBTree.Map.next/1.

Link to this function

pop(rb_map, key, default \\ nil)

View Source

Specs

pop(t(k, v), k, v) :: {v, t(k, v)} when k: key(), v: value()

Returns the value for key and the updated map without key.

If key is present in the map with a value value, {value, new_rb_map} is returned. If key is not present in the map, {default, rb_map} is returned.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> {"Bat", updated} = A.RBMap.pop(rb_map, :b)
iex> updated
#A.RBMap<%{a: "Ant", c: "Cat"}>
iex> {nil, updated} = A.RBMap.pop(rb_map, :z)
iex> updated
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
iex> {"Z", updated} = A.RBMap.pop(rb_map, :z, "Z")
iex> updated
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>

Specs

pop!(t(k, v), k) :: {v, t(k, v)} when k: key(), v: value()

Returns the value for key and the updated map without key.

Behaves the same as pop/3 but raises if key is not present in rb_map.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> {"Bat", updated} = A.RBMap.pop!(rb_map, :b)
iex> updated
#A.RBMap<%{a: "Ant", c: "Cat"}>
iex> A.RBMap.pop!(rb_map, :z)
** (KeyError) key :z not found in: #A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>

Specs

pop_first(t(k, v)) :: {k, v, t(k, v)} | nil when k: key(), v: value()

Finds and pops the {key, value} pair corresponding to the smallest key in rb_map.

Returns a {key, value, new_tree} tuple for non-empty maps, nil for empty maps

Examples

iex> rb_map = A.RBMap.new([b: "B", d: "D", a: "A", c: "C"])
#A.RBMap<%{a: "A", b: "B", c: "C", d: "D"}>
iex> {:a, "A", updated} = A.RBMap.pop_first(rb_map)
iex> updated
#A.RBMap<%{b: "B", c: "C", d: "D"}>
iex> A.RBMap.new() |> A.RBMap.pop_first()
nil

Specs

pop_last(t(k, v)) :: {k, v, t(k, v)} | nil when k: key(), v: value()

Finds and pops the {key, value} pair corresponding to the largest key in rb_map.

Returns a {key, value, new_tree} tuple for non-empty maps, nil for empty maps

Examples

iex> rb_map = A.RBMap.new([b: "B", d: "D", a: "A", c: "C"])
#A.RBMap<%{a: "A", b: "B", c: "C", d: "D"}>
iex> {:d, "D", updated} = A.RBMap.pop_last(rb_map)
iex> updated
#A.RBMap<%{a: "A", b: "B", c: "C"}>
iex> A.RBMap.new() |> A.RBMap.pop_last()
nil
Link to this function

pop_lazy(rb_map, key, fun)

View Source

Specs

pop_lazy(t(k, v), k, (() -> v)) :: {v, t(k, v)} when k: key(), v: value()

Lazily returns and removes the value associated with key in rb_map.

If key is present in rb_map, it returns {value, new_map} where value is the value of the key and new_map is the result of removing key from rb_map. If key is not present in rb_map, {fun_result, rB_map} is returned, where fun_result is the result of applying fun.

This is useful if the default value is very expensive to calculate or generally difficult to setup and teardown again.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> expensive_fun = fn -> "Zebra" end
iex> {"Ant", updated} = A.RBMap.pop_lazy(rb_map, :a, expensive_fun)
iex> updated
#A.RBMap<%{b: "Bat", c: "Cat"}>
iex> {"Zebra", not_updated} = A.RBMap.pop_lazy(rb_map, :z, expensive_fun)
iex> not_updated
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>

Specs

put(t(k, v), k, v) :: v when k: key(), v: value()

Puts the given value under key in rb_map.

If the key does exist, it overwrites the existing value.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.put(rb_map, :b, "Buffalo")
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> A.RBMap.put(rb_map, :d, "Dinosaur")
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat", d: "Dinosaur"}>
Link to this function

put_new(rb_map, key, value)

View Source

Specs

put_new(t(k, v), k, v) :: t(k, v) when k: key(), v: value()

Puts the given value under key unless the entry key already exists in rb_map.

Examples

iex> rb_map = A.RBMap.new(b: "Bat", c: "Cat")
iex> A.RBMap.put_new(rb_map, :a, "Ant")
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
iex> A.RBMap.put_new(rb_map, :b, "Buffalo")
#A.RBMap<%{b: "Bat", c: "Cat"}>
Link to this function

put_new_lazy(rb_map, key, fun)

View Source

Specs

put_new_lazy(t(k, v), k, (() -> v)) :: t(k, v) when k: key(), v: value()

Evaluates fun and puts the result under key in rb_map unless key is already present.

This function is useful in case you want to compute the value to put under key only if key is not already present, as for example, when the value is expensive to calculate or generally difficult to setup and teardown again.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", c: "Cat")
iex> expensive_fun = fn -> "Buffalo" end
iex> A.RBMap.put_new_lazy(rb_map, :b, expensive_fun)
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> A.RBMap.put_new_lazy(rb_map, :a, expensive_fun)
#A.RBMap<%{a: "Ant", c: "Cat"}>
Link to this function

replace(rb_map, key, value)

View Source

Specs

replace(t(k, v), k, v) :: t(k, v) when k: key(), v: value()

Puts a value under key only if the key already exists in rb_map.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.replace(rb_map, :b, "Buffalo")
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> A.RBMap.replace(rb_map, :d, "Dinosaur")
#A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>
Link to this function

replace!(rb_map, key, value)

View Source

Specs

replace!(t(k, v), k, v) :: t(k, v) when k: key(), v: value()

Puts a value under key only if the key already exists in rb_map.

If key is not present in rb_map, a KeyError exception is raised.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.replace!(rb_map, :b, "Buffalo")
#A.RBMap<%{a: "Ant", b: "Buffalo", c: "Cat"}>
iex> A.RBMap.replace!(rb_map, :d, "Dinosaur")
** (KeyError) key :d not found in: #A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>

Specs

size(t()) :: non_neg_integer()

Returns the number of keys in rb_map.

Examples

iex> A.RBMap.size(A.RBMap.new(a: 1, b: 2, c: 3))
3
iex> A.RBMap.size(A.RBMap.new(a: 1, a: 2, a: 3))
1

Returns a new map with all the key-value pairs in rb_map where the key is in keys.

If keys contains keys that are not in rb_map, they're simply ignored.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.take(rb_map, [:c, :e, :a])
#A.RBMap<%{a: "Ant", c: "Cat"}>

Specs

to_list(t(k, v)) :: [{k, v}] when k: key(), v: value()

Returns all values from rb_map.

Examples

iex> rb_map = A.RBMap.new(b: "Bat", c: "Cat", a: "Ant")
iex> A.RBMap.to_list(rb_map)
[a: "Ant", b: "Bat", c: "Cat"]
Link to this function

update(rb_map, key, default, fun)

View Source

Specs

update(t(k, v), k, v, (v -> v)) :: t(k, v) when k: key(), v: value()

Updates the key in rb_map with the given function.

If key is present in rb_map then the existing value is passed to fun and its result is used as the updated value of key. If key is not present in rb_map, default is inserted as the value of key. The default value will not be passed through the update function.

Examples

iex> rb_map = A.RBMap.new(b: "Bat", c: "Cat")
iex>A.RBMap.update(rb_map, :b, "N/A", &String.upcase/1)
#A.RBMap<%{b: "BAT", c: "Cat"}>
iex>A.RBMap.update(rb_map, :a, "N/A", &String.upcase/1)
#A.RBMap<%{a: "N/A", b: "Bat", c: "Cat"}>
Link to this function

update!(rb_map, key, fun)

View Source

Specs

update!(t(k, v), k, v) :: t(k, v) when k: key(), v: value()

Puts a value under key only if the key already exists in rb_map.

If key is not present in rb_map, a KeyError exception is raised.

Examples

iex> rb_map = A.RBMap.new(a: "Ant", b: "Bat", c: "Cat")
iex> A.RBMap.update!(rb_map, :b,  &String.upcase/1)
#A.RBMap<%{a: "Ant", b: "BAT", c: "Cat"}>
iex> A.RBMap.update!(rb_map, :d, &String.upcase/1)
** (KeyError) key :d not found in: #A.RBMap<%{a: "Ant", b: "Bat", c: "Cat"}>

Specs

values(t(key(), v)) :: [v] when v: value()

Returns all values from rb_map.

Examples

iex> rb_map = A.RBMap.new(b: "Bat", c: "Cat", a: "Ant")
iex> A.RBMap.values(rb_map)
["Ant", "Bat", "Cat"]