Dictionaries as alternative to arrays

Tcl arrays are collections of variables, rather than values. This has advantages in some situations (e.g., you can use variable traces on them), but also has a number of drawbacks:

In Tcl 8.5 the dict command has been introduced. This provides efficient access to key-value pairs, just like arrays, but dictionaries are pure values. This means that you can pass them to a procedure just as a list or a string, without the need for dict. Tcl dictionaries are therefore much more like Tcl lists, except that they represent a mapping from keys to values, rather than an ordered sequence.

Unlike arrays, you can nest dictionaries, so that the value for a particular key consists of another dictionary. That way you can elegantly build complicated data structures, such as hierarchical databases. You can also combine dictionaries with other Tcl data structures. For instance, you can build a list of dictionaries that themselves contain lists.

Here is an example (adapted from the man page):

#
# Create a dictionary:
# Two clients, known by their client number,
# with forenames, surname
#
dict set clients 1 forenames Joe
dict set clients 1 surname   Schmoe
dict set clients 2 forenames Anne
dict set clients 2 surname   Other

#
# Print a table
#
puts "Number of clients: [dict size $clients]"
dict for {id info} $clients {
    puts "Client $id:"
    dict with info {
       puts "   Name: $forenames $surname"
    }
}

What happens in this example is:

The order in which elements of a dictionary are returned during a dict for loop is defined to be the chronological order in which keys were added to the dictionary. If you need to access the keys in some other order, then it is advisable to explicitly sort the keys first. For example, to retrieve all elements of a dictionary in alphabetical order, based on the key, we can use the lsort command:

foreach name [lsort [dict keys $mydata]] {
    puts "Data on \"$name\": [dict get $mydata $name]"
}

Example

In this example, we convert the simple database of the previous lessons to work with dictionaries instead of arrays.

#
# The example of the previous lesson revisited - using dicts.
#

proc addname {dbVar first last} {
    upvar 1 $dbVar db

    # Create a new ID (stored in the name array too for easy access)
    dict incr db ID
    set id [dict get $db ID]

    # Create the new record
    dict set db $id first $first
    dict set db $id last  $last
}

proc report {db} {

    # Loop over the last names: make a map from last name to ID

    dict for {id name} $db {
        # Create a temporary dictionary mapping from
        # last name to ID, for reverse lookup
        if {$id eq "ID"} { continue }
        set last       [dict get $name last]
        dict set tmp $last $id
    }

    #
    # Now we can easily print the names in the order we want!
    #
    foreach last [lsort [dict keys $tmp]] {
        set id [dict get $tmp $last]
        puts "   [dict get $db $id first] $last"
    }
}

#
# Initialise the array and add a few names
#
dict set fictional_name ID 0
dict set historical_name ID 0

addname fictional_name Mary Poppins
addname fictional_name Uriah Heep
addname fictional_name Frodo Baggins

addname historical_name Rene Descartes
addname historical_name Richard Lionheart
addname historical_name Leonardo "da Vinci"
addname historical_name Charles Baudelaire
addname historical_name Julius Caesar

#
# Some simple reporting
#
puts "Fictional characters:"
report $fictional_name
puts "Historical characters:"
report $historical_name

Note that in this example we use dictionaries in two different ways. In the addname procedure, we pass the dictionary variable by name and use upvar to make a link to it, as we did previously for arrays. We do this so that changes to the database are reflected in the calling scope, without having to return a new dictionary value. (Try changing the code to avoid using upvar). In the report procedure, however, we pass the dictionary as a value and use it directly. Compare the dictionary and array versions of this example (from the previous lesson) to see the differences between the two data structures and how they are used.