Dictionaries
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$
(written 2012-02-14, updated 2012-02-18)
Associated topics are indexed as below, or enter http://melksh.am/nnnn for individual articles
T208 - Tcl/Tk - Arrays and dicts [122] Passing arrays to procs in Tcl - (2004-11-18)
[779] The fragility of pancakes - and better structures - (2006-06-26)
[1282] Stringing together Tcl scripts - (2007-07-29)
[1283] Generating traffic for network testing - (2007-07-29)
[1405] Sorting in Tcl - lists and arrays - (2007-10-24)
[1427] Arrays in Tcl - a demonstration - (2007-11-10)
[1614] When an array is not an array - (2008-04-17)
[2466] Tcl - passing arrays and strings in and back out of procs - (2009-10-22)
[3192] Tcl - Some example of HOW TO in handling data files and formats - (2011-03-04)
[3415] User defined sorting and other uses of callbacks in Tcl and Tk - (2011-09-02)
[3582] Tcl collections - lists, dicts and array - (2012-01-16)
[3638] Sorting dicts and arrays in Tcl - (2012-03-04)
Some other Articles
lists and struct::list in Tcl - Introduction to struct::list and examplesThe fileutil package and a list of file system commands in TclBus top - colours of LondonHistoric documents for WiltshireTcl - dicts - a tutorial and examplesKeeping Business Local. But is that realistic?Help to get online in MelkshamA customer thanks Well House ManorTraining to do a job, or training to pass an exam?How do classes relate to each other? Associated Classes