Elixir v1.1.0 Dict behaviour

This module specifies the Dict API expected to be implemented by different dictionaries.

It also provides functions that redirect to the underlying Dict, allowing a developer to work with different Dict implementations using one API.

To create a new dict, use the new functions defined by each dict type:

HashDict.new  #=> creates an empty HashDict

In the examples below, dict_impl means a specific Dict implementation, for example HashDict or Map.

Warning

Do not use this module if you expect a certain Dict implementation. For example, if you are working with maps and you don’t need polymorphism, it is preferrable to use the Map module instead of the Dict one.

Protocols

Besides implementing the functions in this module, all dictionaries are required to implement the Access protocol:

iex> dict = dict_impl.new
iex> dict = Dict.put(dict, :hello, :world)
iex> dict[:hello]
:world

As well as the Enumerable and Collectable protocols.

Match

Dictionaries are required to implement all operations using the match (===) operator.

Default implementation

Default implementations for some functions in the Dict module are provided via use Dict.

For example:

defmodule MyDict do
  use Dict

  # implement required functions (see below)
  # override default implementations if optimization
  # is needed
end

The client module must contain the following functions:

All functions, except reduce/3, are required by the Dict behaviour. reduce/3 must be implemented as per the Enumerable protocol.

Based on these functions, Dict generates default implementations for the following functions:

All of these functions are defined as overridable, so you can provide your own implementation if needed.

Note you can also test your custom module via Dict’s doctests:

defmodule MyDict do
  # ...
end

defmodule MyTests do
  use ExUnit.Case
  doctest Dict
  defp dict_impl, do: MyDict
end

Summary

Functions

Removes the entry stored under the given key from dict. If dict does not contain key, returns the dictionary unchanged

Returns a new dict where the given keys are removed from dict. All non-member keys are ignored

Checks if two dicts are equal using ===

Returns {:ok, value} associated with key in dict. If dict does not contain key, returns :error

Returns the value associated with key in dict. If dict does not contain key, it raises KeyError

Returns the value associated with key in dict. If dict does not contain key, returns default (or nil if not provided)

Gets a value from dict and updates the value at key in one pass

Returns the value associated with key in dict. If dict does not contain key, it lazily evaluates fun and returns its result

Returns whether the given key exists in the given dict

Returns a list of all keys in dict. The keys are not guaranteed to be in any order

Merges the dict dict2 into dict dict1

Merges the dict dict2 into dict dict1

Returns the value associated with key in dict as well as the dict without key

Returns the value associated with key in dict as well as the dict without key

Stores the given value under key in dict. If dict already has key, the stored value is replaced by the new one

Puts the given value under key in dict unless key is already present

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

Returns the number of elements in dict

Returns a tuple of two dicts, where the first dict contains only entries from dict with keys in keys, and the second dict contains only entries from dict with keys not in keys

Returns a new dict where only the keys in keys from dict are included

Returns a list of key-value pairs stored in dict. No particular order is enforced

Updates a value in dict by calling fun on the value to get a new value. If key is not present in dict then initial will be stored as the first value

Updates a value in dict by calling fun on the value to get a new value. An exception is generated if key is not present in the dict

Returns a list of all values in dict. The values are not guaranteed to be in any order

Types

key :: any
t :: list | map
value :: any

Functions

delete(dict, key)

Specs

delete(t, key) :: t

Removes the entry stored under the given key from dict. If dict does not contain key, returns the dictionary unchanged.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.delete(dict, :a)
iex> Dict.get(dict, :a)
nil

iex> dict = Enum.into([b: 2], dict_impl.new)
iex> Dict.delete(dict, :a) == dict
true
drop(dict, keys)

Specs

drop(t, [key]) :: t

Returns a new dict where the given keys are removed from dict. All non-member keys are ignored.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.drop(dict, [:a, :c, :d])
iex> Dict.to_list(dict)
[b: 2]

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.drop(dict, [:c, :d])
iex> Dict.to_list(dict) |> Enum.sort
[a: 1, b: 2]
equal?(dict1, dict2)

Specs

equal?(t, t) :: boolean

Checks if two dicts are equal using ===.

Notice this function is polymorphic as it compares dicts of any type. Each dict implementation also provides an equal? function, but they can only compare dicts of the same type.

Examples

iex> dict1 = Enum.into([a: 2, b: 3, f: 5, c: 123], dict_impl.new)
iex> dict2 = [a: 2, b: 3, f: 5, c: 123]
iex> Dict.equal?(dict1, dict2)
true

iex> dict1 = Enum.into([a: 2, b: 3, f: 5, c: 123], dict_impl.new)
iex> dict2 = []
iex> Dict.equal?(dict1, dict2)
false
fetch(dict, key)

Specs

fetch(t, key) :: value

Returns {:ok, value} associated with key in dict. If dict does not contain key, returns :error.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> Dict.fetch(dict, :a)
{:ok, 1}
iex> Dict.fetch(dict, :b)
:error
fetch!(dict, key)

Specs

fetch!(t, key) :: value | no_return

Returns the value associated with key in dict. If dict does not contain key, it raises KeyError.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> Dict.fetch!(dict, :a)
1
get(dict, key, default \\ nil)

Specs

get(t, key, value) :: value

Returns the value associated with key in dict. If dict does not contain key, returns default (or nil if not provided).

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> Dict.get(dict, :a)
1
iex> Dict.get(dict, :b)
nil
iex> Dict.get(dict, :b, 3)
3
get_and_update(dict, key, fun)

Specs

get_and_update(t, key, (value -> {value, value})) :: {value, t}

Gets a value from dict and updates the value at key in one pass.

This fun argument receives the value of key in dict (or nil if key is not present) and must return a two-elements tuple: the “get” value (the value retrieved from the dict which can be operated on before being returned) and the new value to be stored under key in dict.

The returned value is a tuple with the “get” value returned by fun and a new dict with the updated value under key.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> {get, new_dict} = Dict.get_and_update dict, :a, fn(current_value) ->
...>   {current_value + 1, "foo"}
...> end
iex> get
2
iex> Dict.get(new_dict, :a)
"foo"
get_lazy(dict, key, fun)

Specs

get_lazy(t, key, (() -> value)) :: value

Returns the value associated with key in dict. If dict does not contain key, it lazily evaluates fun and returns its result.

This is useful if the default value is very expensive to calculate or generally difficult to set-up and tear-down again.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> fun = fn ->
...>   # some expensive operation here
...>   :result
...> end
iex> Dict.get_lazy(dict, :a, fun)
1
iex> Dict.get_lazy(dict, :b, fun)
:result
has_key?(dict, key)

Specs

has_key?(t, key) :: boolean

Returns whether the given key exists in the given dict.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> Dict.has_key?(dict, :a)
true
iex> Dict.has_key?(dict, :b)
false
keys(dict)

Specs

keys(t) :: [key]

Returns a list of all keys in dict. The keys are not guaranteed to be in any order.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> Enum.sort(Dict.keys(dict))
[:a, :b]
merge(dict1, dict2)

Specs

merge(t, t) :: t

Merges the dict dict2 into dict dict1.

If one of the dict2 entries is found in dict1, the conflicting entries in dict2 have higher precedence.

Notice this function is polymorphic as it merges dicts of any type. Each dict implementation also provides a merge function, but they can only merge dicts of the same type.

Examples

iex> dict1 = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict2 = Enum.into([a: 3, d: 4], dict_impl.new)
iex> dict = Dict.merge(dict1, dict2)
iex> [a: Dict.get(dict, :a), b: Dict.get(dict, :b), d: Dict.get(dict, :d)]
[a: 3, b: 2, d: 4]
merge(dict1, dict2, fun)

Specs

merge(t, t, (key, value, value -> value)) :: t

Merges the dict dict2 into dict dict1.

If one of the dict2 entries is found in dict1, the function will be invoked to resolve the conflict.

Notice this function is polymorphic as it merges dicts of any type. Each dict implementation also provides a merge function, but they can only merge dicts of the same type.

Examples

iex> dict1 = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict2 = Enum.into([a: 3, d: 4], dict_impl.new)
iex> dict = Dict.merge(dict1, dict2, fn(_k, v1, v2) ->
...>   v1 + v2
...> end)
iex> [a: Dict.get(dict, :a), b: Dict.get(dict, :b), d: Dict.get(dict, :d)]
[a: 4, b: 2, d: 4]
pop(dict, key, default \\ nil)

Specs

pop(t, key, value) :: {value, t}

Returns the value associated with key in dict as well as the dict without key.

If key is not present in dict, then the dict will be returned unmodified.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> {v, dict} = Dict.pop dict, :a
iex> {v, Enum.sort(dict)}
{1, []}

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> {v, dict} = Dict.pop dict, :b
iex> {v, Enum.sort(dict)}
{nil, [a: 1]}

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> {v, dict} = Dict.pop dict, :b, 3
iex> {v, Enum.sort(dict)}
{3, [a: 1]}
pop_lazy(dict, key, fun)

Specs

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

Returns the value associated with key in dict as well as the dict without key.

If key is not present in dict, then the dict will be returned unmodified, and it will lazily evaluate fun and return its result instead of the missing value.

This is useful if the default value is very expensive to calculate or generally difficult to set-up and tear-down again.

Examples

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> fun = fn ->
...>   # some expensive operation here
...>   :result
...> end
iex> {v, dict} = Dict.pop_lazy dict, :a, fun
iex> {v, Enum.sort(dict)}
{1, []}

iex> dict = Enum.into([a: 1], dict_impl.new)
iex> fun = fn ->
...>   # some expensive operation here
...>   :result
...> end
iex> {v, dict} = Dict.pop_lazy dict, :b, fun
iex> {v, Enum.sort(dict)}
{:result, [a: 1]}
put(dict, key, val)

Specs

put(t, key, value) :: t

Stores the given value under key in dict. If dict already has key, the stored value is replaced by the new one.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.put(dict, :a, 3)
iex> Dict.get(dict, :a)
3
put_new(dict, key, val)

Specs

put_new(t, key, value) :: t

Puts the given value under key in dict unless key is already present.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.put_new(dict, :a, 3)
iex> Dict.get(dict, :a)
1
put_new_lazy(dict, key, fun)

Specs

put_new_lazy(t, key, (() -> value)) :: t

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

This is useful if the value is very expensive to calculate or generally difficult to set-up and tear-down again.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> fun = fn ->
...>   # some expensive operation here
...>   3
...> end
iex> dict = Dict.put_new_lazy(dict, :a, fun)
iex> Dict.get(dict, :a)
1
iex> dict = Dict.put_new_lazy(dict, :c, fun)
iex> Dict.get(dict, :c)
3
size(dict)

Specs

size(t) :: non_neg_integer

Returns the number of elements in dict.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> Dict.size(dict)
2
split(dict, keys)

Specs

split(t, [key]) :: {t, t}

Returns a tuple of two dicts, where the first dict contains only entries from dict with keys in keys, and the second dict contains only entries from dict with keys not in keys.

All non-member keys are ignored.

Examples

iex> dict = Enum.into([a: 1, b: 2, c: 3, d: 4], dict_impl.new)
iex> {dict1, dict2} = Dict.split(dict, [:a, :c, :e])
iex> {Dict.to_list(dict1) |> Enum.sort, Dict.to_list(dict2) |> Enum.sort}
{[a: 1, c: 3], [b: 2, d: 4]}

iex> dict = Enum.into([], dict_impl.new)
iex> {dict1, dict2} = Dict.split(dict, [:a, :c])
iex> {Dict.to_list(dict1), Dict.to_list(dict2)}
{[], []}

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> {dict1, dict2} = Dict.split(dict, [:a, :b, :c])
iex> {Dict.to_list(dict1) |> Enum.sort, Dict.to_list(dict2)}
{[a: 1, b: 2], []}
take(dict, keys)

Specs

take(t, [key]) :: t

Returns a new dict where only the keys in keys from dict are included.

All non-member keys are ignored.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.take(dict, [:a, :c, :d])
iex> Dict.to_list(dict)
[a: 1]
iex> dict = Dict.take(dict, [:c, :d])
iex> Dict.to_list(dict)
[]
to_list(dict)

Specs

to_list(t) :: list

Returns a list of key-value pairs stored in dict. No particular order is enforced.

Examples

iex> dict = dict_impl.new
iex> dict = Dict.put(dict, :a, 1)
iex> Dict.to_list(dict)
[a: 1]
update(dict, key, initial, fun)

Specs

update(t, key, value, (value -> value)) :: t

Updates a value in dict by calling fun on the value to get a new value. If key is not present in dict then initial will be stored as the first value.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.update(dict, :c, 3, fn(val) -> -val end)
iex> Dict.get(dict, :c)
3
update!(dict, key, fun)

Specs

update!(t, key, (value -> value)) :: t

Updates a value in dict by calling fun on the value to get a new value. An exception is generated if key is not present in the dict.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> dict = Dict.update!(dict, :a, fn(val) -> -val end)
iex> Dict.get(dict, :a)
-1
values(dict)

Specs

values(t) :: [value]

Returns a list of all values in dict. The values are not guaranteed to be in any order.

Examples

iex> dict = Enum.into([a: 1, b: 2], dict_impl.new)
iex> Enum.sort(Dict.values(dict))
[1, 2]

Callbacks

delete(t, key)

Specs

delete(t, key) :: t
drop(t, arg1)

Specs

drop(t, Enum.t) :: t
equal?(t, t)

Specs

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

Specs

fetch(t, key) :: {:ok, value} | :error
fetch!(t, key)

Specs

fetch!(t, key) :: value | no_return
get(t, key)

Specs

get(t, key) :: value
get(t, key, value)

Specs

get(t, key, value) :: value
get_and_update(t, key, list)

Specs

get_and_update(t, key, (value -> {value, value})) :: {value, t}
get_lazy(t, key, list)

Specs

get_lazy(t, key, (() -> value)) :: value
has_key?(t, key)

Specs

has_key?(t, key) :: boolean
keys(t)

Specs

keys(t) :: [key]
merge(t, t)

Specs

merge(t, t) :: t
merge(t, t, list)

Specs

merge(t, t, (key, value, value -> value)) :: t
new()

Specs

new :: t
pop(t, key)

Specs

pop(t, key) :: {value, t}
pop(t, key, value)

Specs

pop(t, key, value) :: {value, t}
pop_lazy(t, key, list)

Specs

pop_lazy(t, key, (() -> value)) :: {value, t}
put(t, key, value)

Specs

put(t, key, value) :: t
put_new(t, key, value)

Specs

put_new(t, key, value) :: t
put_new_lazy(t, key, list)

Specs

put_new_lazy(t, key, (() -> value)) :: t
size(t)

Specs

size(t) :: non_neg_integer
split(t, arg1)

Specs

split(t, Enum.t) :: {t, t}
take(t, arg1)

Specs

take(t, Enum.t) :: t
to_list(t)

Specs

to_list(t) :: list
update(t, key, value, list)

Specs

update(t, key, value, (value -> value)) :: t
update!(t, key, list)

Specs

update!(t, key, (value -> value)) ::
  t |
  no_return
values(t)

Specs

values(t) :: [value]