Skip to content
VLSI Mentor

Tcl Part I · Module 7 · Dictionaries and Arrays

Inspecting an Array: array names, exists, size, parray

Four commands for finding out what an array contains, one of which exists purely to help you while debugging. They are straightforward, and this chapter spends most of its length on a single fact that follows from them and that trips up more Tcl scripts than any other array behaviour: an array has no order whatsoever. Not insertion order, not alphabetical, not stable in any sense you can plan around. Dictionaries guarantee an order and arrays guarantee nothing, and once you have seen what that looks like in practice, sorting stops being a stylistic preference.

Foundation11 min readTclPart IArraysIntrospectionparray

Part I · Module 7 · Chapter 7.10 · Inspecting an Array: array names, exists, size, parray

1. The three questions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set limits {retries 3 timeout 60 mode fast}

puts "exists : [array exists limits]"
puts "size   : [array size limits]"
puts "names  : [lsort [array names limits]]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
exists : 1
size   : 3
names  : mode retries timeout

array exists answers whether the name is an array — not whether it has anything in it, and not whether the name exists at all. array size counts the elements. array names returns them as a list.

Note the lsort. Section 4 explains why it is there.

2. array names takes a pattern

Same glob language as dict keys from Chapter 7.4:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set p {max_fan 16 max_tran 0.4 min_width 2 mode fast}

puts [lsort [array names p max_*]]
puts [lsort [array names p *_*]]
puts "[array names p zzz*]<- nothing"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
max_fan max_tran
max_fan max_tran min_width
<- nothing

Counting matches is then an llength:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [llength [array names p max_*]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2

3. Testing a single element

array names tells you what is there; info exists tells you about one element:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set limits {retries 3}

puts "retries : [info exists limits(retries)]"
puts "timeout : [info exists limits(timeout)]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
retries : 1
timeout : 0

This is the test to use before reading an element you are not sure about — reading a missing one is an error, as Chapter 7.8 §4 showed.

QuestionCommand
Is a an array?array exists a
Is element a(x) set?info exists a(x)
How many elements?array size a
Which elements?array names a ?pattern?

4. An array has no order

Build an array in a deliberate order and ask for the names back:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach x {alpha beta gamma delta epsilon zeta} {
    set A($x) 1
}
puts [array names A]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
zeta beta epsilon delta alpha gamma

That is not alphabetical, not the insertion order, and not reversed. It is the order the internal hash table happens to hold, and the language does not define it. The same thing happens with array get:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set lim {retries 3 timeout 60 mode fast width 8 depth 2}
puts [array get lim]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
width 8 depth 2 mode fast retries 3 timeout 60

Compare with the dictionary from Chapter 7.5 §4, which returns insertion order every single time:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set d [dict create]
foreach x {alpha beta gamma delta epsilon zeta} {
    dict set d $x 1
}
puts [dict keys $d]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
alpha beta gamma delta epsilon zeta

5. Sorting by something other than the name

Because array names gives you a list, every tool from Chapter 6.10 applies — including sorting the names by their values:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set cycles {smoke 120 regress 4500 nightly 18000 lint 60}

proc by_value {arrName a b} {
    upvar 1 $arrName arr
    expr {$arr($a) - $arr($b)}
}

foreach k [lsort -command [list by_value cycles] [array names cycles]] {
    puts [format "%-10s %6d" $k $cycles($k)]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lint           60
smoke         120
regress      4500
nightly     18000

lsort -command passes two elements to your procedure and expects a negative, zero or positive result — the custom-ordering option from Chapter 6.10. Here the elements are names and the comparison looks up their values.

6. parray, for debugging

parray prints an array, sorted and aligned:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set limits {retries 3 timeout 60 mode fast width 8}
parray limits
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
limits(mode)    = fast
limits(retries) = 3
limits(timeout) = 60
limits(width)   = 8

Sorted by name, with the = signs lined up, and each line labelled with the array name — so pasting it into a bug report is self-explanatory. It takes a pattern too:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set P {max_a 1 max_b 2 min_c 3}
parray P max_*
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
P(max_a) = 1
P(max_b) = 2

And it tells you plainly when the name is not an array:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set scalar 1
parray scalar
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
"scalar" isn't an array

7. A debugging walk-through

A small procedure that reports everything knowable about a name:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc describe {name} {
    upvar 1 $name v
    if {![info exists v]} {
        puts "$name: does not exist"
        return
    }
    if {[array exists v]} {
        puts "$name: array with [array size v] element(s)"
        foreach k [lsort [array names v]] {
            puts [format "    %-10s %s" $k $v($k)]
        }
    } else {
        puts "$name: scalar = $v"
    }
}

array set limits {retries 3 timeout 60}
set plain hello

describe limits
describe plain
describe missing
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
limits: array with 2 element(s)
    retries    3
    timeout    60
plain: scalar = hello
missing: does not exist

Three states, three answers. Note the order of the tests: info exists first, because it is true for both kinds, then array exists to tell them apart — the distinction from Chapter 7.8 §6.

8. Exercises

Exercise 1. Given array set a {x 1 y 2 z 3}, which of these are guaranteed to print the same thing on every run and every machine: array size a, array names a, lsort [array names a], array get a?

Exercise 2. Write one line printing the number of elements whose name ends in _max.

Exercise 3. Explain why lsort is optional with dict keys and effectively mandatory with array names.

Exercise 4. What does set out [parray a] leave in out, and what does the script show on screen?

Exercise 5. Write dump — a procedure returning (not printing) a parray-style string for an array, sorted, with values aligned in a column.

Exercise 6. A colleague reports that their script's output rows "move around between runs". The rows come from foreach k [array names results]. Give the diagnosis and the one-word fix.

Answers

1. array size a and lsort [array names a] are guaranteed. array names a and array get a are not — their order is undefined.

2.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set a {w_max 1 h_max 2 depth 3}
puts [llength [array names a *_max]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2

3. A dictionary guarantees insertion order, so dict keys already has a defined, reproducible order and lsort merely changes it to a different one. An array guarantees nothing, so without lsort the order is arbitrary and may change — there is no correct order to fall back on.

4. out is empty, because parray returns nothing. The array still prints on screen, because parray writes directly to standard output as a side effect.

5.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc dump {name} {
    upvar 1 $name a
    set lines {}
    set w 0
    foreach k [array names a] {
        if {[string length $k] > $w} { set w [string length $k] }
    }
    foreach k [lsort [array names a]] {
        lappend lines [format "%-*s = %s" $w $k $a($k)]
    }
    return [join $lines "\n"]
}

array set limits {retries 3 timeout 60 mode fast}
puts [dump limits]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
mode    = fast
retries = 3
timeout = 60

%-*s takes its width from an argument — the trick from Chapter 5.11.

6. The diagnosis is that array names has no defined order, so the rows can come out differently on different runs or interpreters. The fix is lsort.

9. Remember

  • array exists a asks whether the name is an array; array size a counts elements; array names a ?pattern? lists them.
  • info exists a(x) is the test for a single element, and the one to use before reading something that may be absent.
  • An array has no order at all. array names and array get return whatever the hash table holds, and it is not defined by the language.
  • Dictionaries guarantee insertion order; arrays guarantee nothing. lsort is a choice for one and part of the idiom for the other.
  • Never let unsorted array names output reach a report or a file.
  • lsort -command sorts names by their values, since array names returns an ordinary list.
  • parray prints sorted, aligned output for debugging — it takes a pattern, says "x" isn't an array when it should, and returns nothing, so you cannot capture it.

Where this fits

Part of the Tcl curriculum.