Tcl Part I · Module 7 · Dictionaries and Arrays
Reading and Writing Array Elements
The previous chapter established what an array is. This one gives you the working vocabulary: the two commands that load a whole array from a list and flatten it back out again, the one that removes things, and the ordinary variable syntax that handles everything in between. Two of these behave in ways people guess wrong about — array set adds to what is already there rather than replacing it, and array unset does two quite different jobs depending on how many arguments you give it. Both are easy once seen, and expensive when discovered by accident.
Foundation11 min readTclPart IArraysarray setarray get
Part I · Module 7 · Chapter 7.9 · Reading and Writing Array Elements
1. One element at a time
Elements are variables, so the ordinary commands work:
set limits(retries) 3
set limits(timeout) 60
puts $limits(retries)
incr limits(retries)
puts $limits(retries)
append limits(note) "checked"
append limits(note) " twice"
puts $limits(note)3
4
checked twiceNothing array-specific is happening. incr and append are the same commands from Chapter 3.9 and Chapter 5.10, operating on variables that happen to have parentheses in their names.
2. Loading a whole array at once
array set takes a flat list of alternating names and values:
array set limits {retries 3 timeout 60 mode fast}
puts $limits(retries)
puts $limits(mode)3
fastThat list is exactly the shape of a dictionary — which is the bridge Chapter 7.12 is built on.
An odd number of elements is rejected:
array set bad {a 1 b}list must have an even number of elements3. array set merges
Here is the one people guess wrong:
array set m {a 1 b 2}
array set m {b 20 c 3}
foreach k [lsort [array names m]] {
puts "m($k) = $m($k)"
}m(a) = 1
m(b) = 20
m(c) = 3a survived. The second array set did not replace the array — it set its two pairs into the array that was already there, overwriting b and adding c.
This is usually what you want, and it makes array set a natural way to apply defaults then override them:
array set opt {retries 1 timeout 30 verbose 0}
array set opt {timeout 300}
puts "retries=$opt(retries) timeout=$opt(timeout) verbose=$opt(verbose)"retries=1 timeout=300 verbose=0Defaults first, then the user's settings. Three lines, no loop.
4. Flattening it back out
array get is the inverse of array set:
array set limits {retries 3 timeout 60 mode fast}
puts [array get limits]mode fast retries 3 timeout 60A flat list of alternating names and values — the same shape you put in, and the same shape a dictionary is.
The order is not the order you wrote. It is whatever order the array's internal table happens to hold, and the language does not define it. Your interpreter may print those three pairs in a different order from this page, and that is not a bug. Chapter 7.10 deals with this properly; for now, never depend on it.
Both array get and array names take an optional glob pattern — the same language as dict keys in Chapter 7.4:
array set p {max_fan 16 max_tran 0.4 mode fast}
puts [lsort [array names p max_*]]
puts [array get p max_*]max_fan max_tran
max_tran 0.4 max_fan 16Note the lsort on the first line and not the second: sorting a list of names is easy, and sorting a flat pair list is not — which is the first sign that array get is for handing data onward, not for display.
5. Removing things
array unset does two jobs. With a pattern, it removes matching elements:
array set p {max_fan 16 max_tran 0.4 mode fast}
array unset p max_*
puts [array names p]modeWith no pattern, it removes the entire array:
array unset p
puts "exists: [array exists p]"exists: 0A single element goes with ordinary unset:
array set q {a 1 b 2}
unset q(a)
puts [array names q]b6. Elements hold any string, including structures
An element is a variable, so it holds a string, so it can hold a list or a dictionary:
set data(ports) [list clk rst din]
set data(cfg) [dict create width 8 depth 16]
puts "ports: [llength $data(ports)] -> [lindex $data(ports) 1]"
puts "width: [dict get $data(cfg) width]"ports: 3 -> rst
width: 8This is how arrays hold nested structure: the value nests, even though the array itself cannot. Updating one needs the usual get–modify–set, because the element is a value like any other:
dict set data(cfg) width 32
puts [dict get $data(cfg) width]32That works directly, because dict set takes a variable name and data(cfg) is a variable name.
7. A worked example
Reading options, applying defaults, reporting:
proc show_opts {name} {
upvar 1 $name opt
foreach k [lsort [array names opt]] {
puts [format " %-10s %s" $k $opt($k)]
}
}
# defaults, then overrides
array set opt {retries 1 timeout 30 verbose 0 mode slow}
array set opt {timeout 300 mode fast}
puts "settings ([array size opt]):"
show_opts opt
array unset opt verbose
puts "after dropping verbose ([array size opt]):"
show_opts optsettings (4):
mode fast
retries 1
timeout 300
verbose 0
after dropping verbose (3):
mode fast
retries 1
timeout 300lsort appears in show_opts for the reason section 4 gave: without it, the output order is arbitrary. In a report, that is not acceptable, so sorting is not a nicety here.
8. Exercises
Exercise 1. After these two lines, what are all the elements and values?
array set a {x 1 y 2}
array set a {y 20 z 3}Exercise 2. Write the two lines that completely replace an array's contents with {p 1 q 2}, discarding whatever was there.
Exercise 3. What is the error from array set a {1 2 3}, and why?
Exercise 4. Explain the difference between unset a(x), array unset a x* and array unset a.
Exercise 5. Store a dictionary in an array element, then change one of its keys, and print the result. Explain why dict set a(cfg) k v is allowed.
Exercise 6. Write defaults — a procedure that takes an array name and a flat list of default pairs, and sets only the elements that are not already present.
Answers
1. x=1, y=20, z=3. array set merges: x survives, y is overwritten, z is added.
2.
array unset a
array set a {p 1 q 2}3. list must have an even number of elements. array set needs alternating names and values, and three items cannot pair up.
4. unset a(x) removes exactly the element x. array unset a x* removes every element whose name matches the pattern. array unset a removes the whole array, after which array exists a is 0.
5.
set a(cfg) [dict create width 8]
dict set a(cfg) width 32
puts $a(cfg)width 32dict set needs a variable name, and a(cfg) is the name of a real variable — the point of Chapter 7.8 §2. Nothing special is needed.
6.
proc defaults {name pairs} {
upvar 1 $name a
foreach {k v} $pairs {
if {![info exists a($k)]} { set a($k) $v }
}
}
array set opt {timeout 300}
defaults opt {retries 1 timeout 30 verbose 0}
foreach k [lsort [array names opt]] { puts "$k = $opt($k)" }retries = 1
timeout = 300
verbose = 0timeout kept the value it already had. Note this is the opposite order from section 3's idiom — here the defaults are applied after, so they must not overwrite.
9. Remember
- Elements are variables.
set,incr,appendandunsetall work on them directly. array set name listloads a whole array from alternating names and values; an odd count islist must have an even number of elements.array setmerges — it does not replace. To replace,array unsetfirst.- That merge makes defaults easy: defaults first, overrides second.
array get nameflattens to a dictionary-shaped list, in an order the language does not define. Never rely on it.- Both
array getandarray namestake a glob pattern, same language asdict keys. array unset a patremoves matching elements;array unset aremoves everything. One argument apart.array set name {}is the only way to make an empty array.- An element can hold a list or a dictionary, and
dict set a(cfg) k vworks becausea(cfg)is a variable name.
Where this fits
Part of the Tcl curriculum.
