Tcl - dicts - a tutorial and examples
Archive - Originally posted on "The Horse's Mouth" - 2012-02-14 22:15:15 - Graham EllisDictionaries
At Tcl 8.5, the dict command was added to Tcl.
What is a dictionary
A dict used the same formatting as a list - in other words, it's a string of text (just like every other variable type except an array) which is handled through the same tokeniser / interpreter that's used for the source code of Tcl. However, in the using of the dict command, list members are taken as alternate key / value pairs. In effect, that gives you the capabilities of an array but with the flexibility of handling your data as a pure string too - in the right circumstances, it's the best of both worlds.
For example, I can set up a dictionary or a list as follows:
set demo {Anne Jones Bill Brown Charlotte Steeple Dennis {The Menace} Enid Blyton
Fred Dibnah Gwynn Grimm Henry Eighth Isobel Quietly}
and access a member via its key with dict:
set surname [dict get $demo Enid]
puts $surname
which runs like this:
wizard:textra graham$ tclsh dict1
Blyton
wizard:textra graham$
[source link]
and equally I can access members via position (a list):
set surname [lindex $demo 9]
puts $surname
which runs like this:
wizard:textra graham$ tclsh dict2
Blyton
wizard:textra graham$
[source link]
Iterating through a dictionary
dict keys will return you a list of keys (very useful) and dict values will return you a list of values - rarely what you want. Here's dict keys in use:
set demo {Anne Jones Bill Brown Charlotte Steeple Dennis {The Menace} Enid Blyton
Fred Dibnah Gwynn Grimm Henry Eighth Isobel Quietly}
foreach item [dict keys $demo] {
set val [dict get $demo $item]
puts "We have $item with a value $val"
}
[source link]
And the sample output:
wizard:textra graham$ tclsh dict3
We have Anne with a value Jones
We have Bill with a value Brown
We have Charlotte with a value Steeple
We have Dennis with a value The Menace
We have Enid with a value Blyton
We have Fred with a value Dibnah
We have Gwynn with a value Grimm
We have Henry with a value Eighth
We have Isobel with a value Quietly
wizard:textra graham$
There is also a dict for command which lets you iterate through key, value pairs and run a block of code on each pair - so it's actually a new loop:
set demo {Anne Jones Bill Brown Charlotte Steeple Dennis {The Menace} Enid Blyton
Fred Dibnah Gwynn Grimm Henry Eighth Isobel Quietly}
dict for {thekey theval} $demo {
puts "We have $thekey with a value $theval"
}
(example dict4) which produces the same output as the example above.
[source link]
dictionary manipulation commands
* You can pass a dict into a proc without using upvar!
proc timetable {type schedule} {
puts "\n----------> $type"
dict for {where when} $schedule {
puts [format "%-25s %11s" $where $when]
}
}
set mydata [dict create "Cardiff Central" 06:28 Newport 06:42 \
"Severn Tunnel Junction" 06:53 Pilning 07:03 Patchway 07:06]
dict lappend mydata {Filton Abbey Wood} 07:09
set moredata [dict create "Bristol Temple Meads" dunno Bath\ Spa 07:35 \
Bradford-on-Avon 07:47 Trowbridge 07:53 Westbury 08:00]
# Merge two dictionaries
set allway [dict merge $mydata $moredata]
timetable original $allway
# lappend only adds ONE item - so this has to be 2 separate commands
dict lappend allway Warminster 08:09
dict lappend allway Salisbury 08:32
# Add to existing value within the dictionary
dict append allway Westbury -08:01
# Remove an item from the dictionary
set allway [dict remove $allway Pilning]
# Replace a value
set allway [dict replace $allway {Bristol Temple Meads} 07:22]
timetable modified $allway
# search by value and return a shorter dict
set before_8 [dict filter $allway value {0[0-7]*} ]
timetable "before 8 a.m." $before_8
# How many items?
set sz [dict size $before_8]
puts "There are $sz items in that dict"
[source link]
Let's see what I have used that's new:
dict create - create a new dictionary
dict append - append a string to a value in a dictionary
dict lappend - add a new key, value pair to a dictionary
dict merge - concatenate two dictionaries, but remove duplicate keys
dict remove - remove a pair based on its key
dict replace - replace a value based on its key
dict filter - return a small dictionary based on a match to a key or a value
dict size - tell me how many pairs there are in a dictionary
It runs like this:
wizard:textra graham$ tclsh dicdem
----------> original
Cardiff Central 06:28
Newport 06:42
Severn Tunnel Junction 06:53
Pilning 07:03
Patchway 07:06
Filton Abbey Wood 07:09
Bristol Temple Meads dunno
Bath Spa 07:35
Bradford-on-Avon 07:47
Trowbridge 07:53
Westbury 08:00
----------> modified
Cardiff Central 06:28
Newport 06:42
Severn Tunnel Junction 06:53
Patchway 07:06
Filton Abbey Wood 07:09
Bristol Temple Meads 07:22
Bath Spa 07:35
Bradford-on-Avon 07:47
Trowbridge 07:53
Westbury 08:00-08:01
Warminster 08:09
Salisbury 08:32
----------> before 8 a.m.
Cardiff Central 06:28
Newport 06:42
Severn Tunnel Junction 06:53
Patchway 07:06
Filton Abbey Wood 07:09
Bristol Temple Meads 07:22
Bath Spa 07:35
Bradford-on-Avon 07:47
Trowbridge 07:53
There are 9 items in that dict
wizard:textra graham$
To more or less complete the story, you also have
dict incr - to increment a value within a dict. Very useful indeed for counters
dict info - information string about the dict
dict exists - to check whether a key exists
Nested Dictionaries
Just as you can have lists within lists, you can have dicts within dicts. And indeed some of the dict subcommands actually provide extra support for this. Here's an example:
dict set course Tcl Learning 4
dict set course Tcl Programming 3
dict set course Perl Learning 5
dict set course Perl Programming 5
dict set course Perl Advanced 3
dict set course Python Learning 4
dict set course Python Programming 3
puts $course
dict for {language levels} $course {
puts "$language courses"
dict with levels {
puts "--- First level $Learning"
puts "--- Second level $Programming"
if {[dict exists $course $language Advanced]} {
puts "--- Third level $Advanced"
} else {
puts "--- Third level not offered"
}
}
}
[source link]
Which runs as follows:
wizard:textra graham$ tclsh nested
Tcl {Learning 4 Programming 3} Perl {Learning 5 Programming 5 Advanced 3} Python {Learning 4 Programming 3}
Tcl courses
--- First level 4
--- Second level 3
--- Third level not offered
Perl courses
--- First level 5
--- Second level 5
--- Third level 3
Python courses
--- First level 4
--- Second level 3
--- Third level not offered
wizard:textra graham$