In Erlang, dictionaries (sometimes called hashes or maps in other languages) are not fundamental parts of the language (unlike lists, strings or tuples). You can include them in your Erlang modules by using the dict library. We’ll show you how.
Dictionaries are data structures that provide a mapping between keys and values. Like lists, they are another way of storing variable amounts of information, but differ in that you can retrieve random elements in constant time. If retrieval speed is more important than insertion speed, and your program needs random access, then you should consider using dictionaries.
Just like a human language dictionary, you can use a dictionary to store information that can be looked up by name. Another use is to replace arrays that will only be partially full, called sparse arrays, with a dictionary using the index as a key — this allows you to store the array in less memory than would otherwise be required.
All dictionary functionality resides in the dict module. Use dict:new to create a new dictionary:
1> dict:new(). {dict,0, 16, 16, 8, 80, 48, {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}, {{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}
This demonstrates the way Erlang represents dictionaries internally, as a series of nested tuples and lists. Don’t worry too much about this, there’s no need to change this structure manually, you should use the functions in the dict module for that.
There are two things you should take from this structure — the first is that dict objects are identified by the atom “dict” as the first element in the tuple, you can use this to identify dictionary arguments. The second useful piece of information is that the second element is equal to the number of keys in the list.
You can also create dictionaries with a given set of key->value pairs by using the from_list function. Just give it a list of two element tuples (the first element being the key, the second the value) as an argument:
2> dict:from_list([{list, 1},{tuple, 2}, {string, 3}]). {dict,3, ...more
Likewise, you can use the to_list function to convert a dict to a list. Be warned, the order in which the elements are returned by this method is not guaranteed to have any relation to the order in which they were inserted.
3> dict:to_list( dict:from_list([{list, 1}, {tuple, 2}, {string, 3}]) ). [{list,1},{tuple,2},{string,3}]
Adding entries
You can add entries to the dictionary using the store function. Store takes a key, the new value and the dictionary and returns a new dictionary which is updated with that information. Because tuples in Erlang are immutable, the interpreter cannot modify the previous dictionary and must create a new one.
3> dict:to_list( dict:store(item, value, dict:new()) ). [{item,value}]
What if you want to have multiple values associated with a single key. You can store a list as a value, but updating this list requires fetching the value, modifying it and then storing the modified list. This is such a common operation that there is an append function in the dict module.
4> dict:to_list( dict:append(item, value, dict:append(item, value2, dict:new()) ) ). [{item,[value2,value]}]
If you’ve got a whole list of values to append at once, you can use the append_list function:
5> dict:to_list( dict:append_list(item, [value, value2], dict:new()) ). [{item,[value,value2]}]
Finding and fetching entries
To get the value of a key in the list, you can use either the find or fetch functions. The find function is better used when you are uncertain if the key exists within the dictionary, whereas the fetch function is easier if you expect the key to exist. Fetch will throw an exception if the given key is not contained within the dictionary.
6> Dict = dict:from_list([{item, [value1,value2]}, {thing, value3}, {stuff, value4}]) {dict,3, ...more 7> dict:find(item, Dict). {ok,[value1,value2]} 8> dict:find(noitem, Dict). error 9> dict:fetch(thing, Dict). value3 10> dict:fetch(nothing, Dict). =ERROR REPORT==== 24-Oct-2007::15:17:24 === Error in process <0.41.0> with exit value: {function_clause,[{dict,fetch_val,[nothing, []]},{erl_eval,do_apply,5},{shell,exprs,6},{shell,eval_loop,3}]} ** exited: {function_clause,[{dict,fetch_val,[nothing,[]]}, {erl_eval,do_apply,5}, {shell,exprs,6}, {shell,eval_loop,3}]} **
map, filter and fold for dicts
The dict module also contains versions of the map, filter and fold functions that work on dictionaries. If you’re familiar with the standard version that act on lists they should be straightforward enough, the only difference being that they return dictionaries. The order of the elements that they work on is undefined.
11> dict:map( fun(K,V) -> io:format("~w -> ~w~n", [K,V]) end, Dict). stuff -> value4 thing -> value3 item -> [value1,value2] 12> dict:to_list( dict:filter( fun(K,V) -> is_atom(V) end, Dict)). [{stuff,value4},{thing,value3}] 13> dict:fold( fun(K,V,A) -> atom_to_list(K)++A end, "", Dict). "stuffthingitem"
That’s all you need to know to deal with dicts in Erlang. There is another data structure that has a similar nature, ets tables, which are designed for large amounts of data and heavy use — but we’ll leave those to another article.