Skip to content
VLSI Mentor

Tcl Part I · Module 7 · Dictionaries and Arrays

dict keys, dict values, dict size

Three small commands that tell you what is in a dictionary without reading it key by key. They are easy, and two things about them are worth more than the syntax: they each accept a glob pattern, which turns a four-line loop into one line and which most Tcl programmers never discover; and the way you count a dictionary is not the way you count a list, even though a dictionary is one. This chapter settles both, and it reuses the pattern language you already learned twice in earlier modules.

Foundation10 min readTclPart IDictionariesIntrospectionGlob patterns

Part I · Module 7 · Chapter 7.4 · dict keys, dict values, dict size

1. The three commands

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cfg [dict create max_fanout 16 max_tran 0.4 mode fast runs 3]

puts [dict keys $cfg]
puts [dict values $cfg]
puts [dict size $cfg]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
max_fanout max_tran mode runs
16 0.4 fast 3
4

dict keys and dict values each return a list, which means everything from Module 6 applies to the result — lsort, lsearch, foreach, llength. dict size returns the number of pairs.

All three take a value, not a variable name. None of them modifies anything.

2. The order is the insertion order

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set d [dict create]
foreach k {zebra apple mango} {
    dict set d $k 1
}
puts [dict keys $d]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
zebra apple mango

Not alphabetical — the order you put them in. Chapter 7.2 §3 established this guarantee, and here is where you actually use it: if you built a dictionary in a meaningful order, dict keys hands it back in that order, and a report built from it comes out the way you intended.

If you want alphabetical, ask for it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [lsort [dict keys $d]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
apple mango zebra

3. The pattern argument

Each of dict keys and dict values takes an optional glob pattern:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cfg [dict create max_fanout 16 max_tran 0.4 mode fast runs 3]

puts [dict keys $cfg max_*]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
max_fanout max_tran

That is the same pattern language as string match from Chapter 5.5 and the default mode of lsearch from Chapter 6.9: * for any run of characters, ? for exactly one, [...] for a character set. Nothing new to learn.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [dict keys $cfg *a*]
puts [dict keys $cfg mode]
puts "[dict keys $cfg zzz*]<- nothing matched"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
max_fanout max_tran
mode
<- nothing matched

No match gives an empty list, not an error — the same behaviour lsearch has.

Compare the one-liner with the loop it replaces:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# the long way
set found {}
foreach k [dict keys $cfg] {
    if {[string match max_* $k]} { lappend found $k }
}
puts $found

# the short way
puts [dict keys $cfg max_*]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
max_fanout max_tran
max_fanout max_tran

4. dict values filters on the value

This is the one that misleads people:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [dict values $cfg f*]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fast

The pattern applied to the values, not the keys. fast is a value; mode is its key. It is consistent — dict keys patterns keys, dict values patterns values — and it reads wrong at a glance because you are usually thinking in keys.

5. Counting: pairs versus words

Chapter 7.1 showed that a dictionary is literally its own flat text. So there are two defensible answers to "how big is it?", and they differ:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cfg [dict create mode fast runs 3]

puts "llength   : [llength $cfg]"
puts "dict size : [dict size $cfg]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
llength   : 4
dict size : 2

Four words; two pairs. dict size is almost always the one you want, and llength on a raw dictionary is almost always a bug — it is the dictionary version of the string length versus llength contrast that opened Chapter 6.1.

Counting the result of dict keys is fine, because that really is a list:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [llength [dict keys $cfg]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2

And it stays correct even when values contain spaces, because dict values quotes properly — the automatic quoting from Chapter 6.2:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set sp [dict create a "two words" b x]
puts "values     : [dict values $sp]"
puts "llength    : [llength [dict values $sp]]"
puts "dict size  : [dict size $sp]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
values     : {two words} x
llength    : 2
dict size  : 2

Both say 2. The raw text would not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [llength $sp]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4

6. Emptiness

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set e [dict create]

puts "size   : [dict size $e]"
puts "keys   : '[dict keys $e]'"
puts "empty? : [expr {[dict size $e] == 0}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
size   : 0
keys   : ''
empty? : 1

dict size is the clear test. Comparing the dictionary to "" also happens to work, because an empty dictionary is the empty string — but it reads as a string comparison and will confuse the next person.

7. Putting them together

A tidy settings report, sorted, aligned, and filtered:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cfg [dict create max_fanout 16 max_tran 0.4 mode fast runs 3 width 8]

puts "[dict size $cfg] settings"
foreach k [lsort [dict keys $cfg]] {
    puts [format "  %-12s %s" $k [dict get $cfg $k]]
}

puts "limits only:"
foreach k [lsort [dict keys $cfg max_*]] {
    puts [format "  %-12s %s" $k [dict get $cfg $k]]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
5 settings
  max_fanout   16
  max_tran     0.4
  mode         fast
  runs         3
  width        8
limits only:
  max_fanout   16
  max_tran     0.4

format with %-12s is from Chapter 5.11. The whole report is four commands you already knew and one you just met.

The next chapter shows a shorter way to write that loop — one that hands you the key and the value together, without a dict get inside the body.

8. Exercises

Exercise 1. Given set d [dict create b 2 a 1 c 3], what do dict keys $d and lsort [dict keys $d] print?

Exercise 2. Write one line that prints how many keys in $cfg begin with max_.

Exercise 3. Explain why llength $cfg and dict size $cfg disagree, and which one is nearly always the bug.

Exercise 4. What does dict values $cfg 1* return for set cfg [dict create a 16 b 0.4 c 100], and why is the answer about values rather than keys?

Exercise 5. Write a procedure summary that returns a one-line string like 3 keys: a, b, c with the keys in alphabetical order.

Exercise 6. Someone writes if {$cfg eq ""} { puts "empty" } to test for an empty dictionary. It works. Give the better test and one reason to prefer it.

Answers

1. b a c — insertion order. Then a b c.

2.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cfg [dict create max_fanout 16 max_tran 0.4 mode fast]
puts [llength [dict keys $cfg max_*]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2

3. llength reads the dictionary as a flat list of words, so it counts keys and values; dict size counts pairs. llength on a raw dictionary is nearly always the bug — it reports double the real size whenever every value is a single word, and something else entirely when they are not.

4.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cfg [dict create a 16 b 0.4 c 100]
puts [dict values $cfg 1*]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
16 100

dict values matches the pattern against the values, so 16 and 100 qualify and 0.4 does not. The keys are never consulted.

5.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc summary {d} {
    set ks [lsort [dict keys $d]]
    return "[llength $ks] keys: [join $ks {, }]"
}
puts [summary [dict create c 3 a 1 b 2]]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 keys: a, b, c

6. if {[dict size $cfg] == 0}. It says what it means — an empty dictionary is a size question, not a string question — and it does not quietly rely on the coincidence that the empty dictionary and the empty string are the same text.

9. Remember

  • dict keys and dict values return lists, so every list command from Module 6 works on the result.
  • The order is insertion order, guaranteed. Use lsort when you want alphabetical.
  • Both take an optional glob pattern — the same language as string match and lsearch. No match gives an empty list.
  • dict values $d pat matches the values, not the keys of those values. To get values for matching keys, pattern the keys and look each one up.
  • dict size counts pairs; llength on a raw dictionary counts words and is nearly always wrong.
  • llength [dict keys $d] is fine — that really is a list.
  • Test emptiness with dict size, not by comparing to "".

Where this fits

Part of the Tcl curriculum.