3 Lists and Simple Data

3.4 Property Lists and Hashes

3.4.1 Property Lists

Property lists are just lists whose entries are key/value tuples. Alternatively, an entry may be a single atom, in which case it implies a tuple with the atom as the key and true as the value.

Since there's no special type here, we just create a regular list:

> (set plist
    (list
      (tuple '|to see|
             '"moons of Jaglan Beta"
             '"beaches of Santraginus V")
      (tuple '|to avoid|
             '"small piece of fairy cake")))
>

Let's see what keys we have defined:

> (: proplists get_keys plist)
(|to avoid| |to see|)
>

Extracting data by key:

> (: proplists lookup '|to see| plist)
#(|to see| "moons of Jaglan Beta" "beaches of Santraginus V")
> (: proplists lookup '|to avoid| plist)
#(|to avoid| "small piece of fairy cake")
>

If you know that your value is single-valued (e.g., not a list), then you can do this:

> (: proplists get_value '|to avoid| plist)
"small piece of fairy cake"
>

There is more information about property lists on the docs page for them.

3.4.2 Hashes

There is no builtin "dictionary" or "hash" type in Erlang. However, there are some libraries that support data structures like these. There is also a concept of "records" which we will discuss in another section.

3.4.2.1 The Dictionary

The Erlang dict module implements a key/value dictionary part of which is an additional dict data type which supplements the built-in Erlang data types.

Here's how you create a new dict:

> (set my-dict (: dict new))
#(dict
  0
  16
  16
  8
  80
  48
  #(() () () () () () () () () () () () () () () ())
  #(#(() () () () () () () () () () () () () () () ())))
>

Let's check that there's no actual data in it:

> (: dict size my-dict)
0

Now let's add some!

> (set my-dict
    (: dict append '|to see| '"moons of Jaglan Beta" my-dict))
#(dict ...
> (set my-dict
    (: dict append '|to avoid| '"small piece of fairy cake" my-dict))
#(dict ...
>

As you might guess from the usage, dicts are not updated in-place. A new dictionary is returned with each call to append. As such, we need to reset with each append. Is everything there?

> (: dict size my-dict)
2
>

Looking good so far... Now let's get some data out:

> (: dict fetch '|to avoid| my-dict)
("small piece of fairy cake")
>

Why the is the function called "append"? Well, dict accepts multiple values for keys. Let's try this out, and then re-query our dict:

> (: erlang length (: dict fetch '|to see| my-dict))
1
> (set my-dict
    (: dict append '|to see| '"beaches of Santraginus V" my-dict))
#(dict ...
> (: erlang length (: dict fetch '|to see| my-dict))
2
> (: dict size my-dict)
2
> (: dict fetch '|to see| my-dict)
("moons of Jaglan Beta" "beaches of Santraginus V")
>

The size of the my-dict didn't change because we didn't add a new key; rather, we updated an existing one, appending a new value. The |to see| key now has two values in it.

You can also build dicts from a list of tuples:

> (set my-dict-2
    (: dict from_list '(#('key1 '"foo") #('key2 '"bar"))))
#(dict ...
> (: dict size my-dict-2)
2
>

There are many more functions to explore in the dict docs.

3.4.2.2 Other Hash Tables

OTP comes with the ets module which provides the ability to store very large quantities of data in an Erlang runtime system. The ets module supports hash tables of the following types: * set * ordered_set * bag * duplicate_bag

The documentation for this module is here, though we will be adding information on how to use this from LFE at a later point (likely a dedicated tutorial).