dict tips and tricks describes various techniques for working with dictionaries.
As of version 8.5, items in a dictionary are ordered according to the sequence in which they were added. dict routines return results according to that order:
proc dict'sort {dict args} { set res {} foreach key [lsort {*}$args [dict keys $dict]] { dict set res $key [dict get $dict $key] } set res }
example:
set d1 {foo 1 bar 2 grill 3} puts 1:[dict'sort $d1] ;# 1:bar 2 foo 1 grill 3 puts 2:[dict'sort $d1 -decreasing] ;# 2:grill 3 foo 1 bar 2
This works for me correctly for Tcl 8.4 version posted by kruzalex
sugar::proc dict'sort {dict args} { set res {} foreach key [lsort {*}$args [dict keys $dict]] { lappend a() $key [dict get $dict $key] } set res [lindex [array get a] 1] }
or without sugar::proc
proc dict'sort {dict args} { set res {} foreach key [eval [list lsort] [lrange $args 0 end] [list [dict keys $dict]]] { lappend a() $key [dict get $dict $key] } set res [lindex [array get a] 1] }
A dictionary can be used to set an array:
array set X [[dict] filter ...]
And array get produces a dict:
dict get [[array] get X] key
In a procedure, $args can be treated as a dictionary if its length is an even number:
proc fred args { dict get $args something }
A simple way of dealing with options is to treat $args as a dictionary:
# returns an error if $args is not a dictionary dict size $args foreach {opt val} $args { switch $opt { someoption { ... } someotheroption { ... } } }
See Command Options for further information on the topic.
Consider :
set x {one 1 two 2} dict with x {} puts [list $one $two]
dict with assigned all the items in $x to variables. This is convenient for passing named values between routines.
JMN 2008-06-20 PYK 2019-10-16: A dictionary can be extended using lappend. For the case of a loop where you know the newly added keys are not currently in the dictionary - might this be faster than using dict set? e.g
foreach val $newValues { lappend mydict [uuid::uuid generate] $val }
or
lappend mydict {*}$newPairs
Since lappend is a list operation, it is possible use it to add redundant keys to a dictionary, and routines like dict get, dict size only consider the last identically-named item as an entry in the dictionary. Routines such as dict set that modify the dictionary remove all previous duplicate entries.
Shimmering between a list and a dictionary carries some cost, but presumably in the above case the lappend would still be a win for large datasets because the existence of the key doesn't need to be checked each time a new value is added. Perhaps this gain is lost anyway once the dictionary is converted back to a proper dictionary value.
I've not had a chance to test the relative performance of this yet... so don't consider it as a tip/trick til you've verified it helps for your particular case!
In particular - it might be worth comparing the above with:
set mydict [dict merge $mydict[set mydict {}] $newPairs]
update: A few rough tests indicate that the lappend method is actually slower. The foreach loop using lappend does indeed run faster than dict set - but this time (and more!) is lost during the subsequent access of the value as a dictionary using dict size $mydict
For Tcl8.6a0 at least - it would seem the moral is, if you're going to be using it as a dictionary, just build it as a dictionary using the dict routines.
HaO 2010-06-28: I would like a dict subcommand which determines whether a value is a dictionary similar to array exists. The pdict example uses:
if { [catch {dict keys ${d}}] } { error "error: pdict - argument is not a dict" }
which is ok but might pollute the error log as a side effect. Is there a more elegant solution ?
AMG: You can use [string is list] plus a test for even [llength].
if {![string is list $d] || ([llength $d] & 1)} { error "not a dict" }
APN: The causes the value to shimmering though.
CMcC: FWIW, I have used if {![catch {dict size $d}]} {...} to test for dictness.
gasty: Simple procedure to check if a key exists in a dictionary and return their value:
proc getDictItem {dictVal keyVar} { upvar $keyVar keyVal if {[dict exists $dictVal $keyVar]} { set keyVal [dict get $dictVal $keyVar] return 1 } return 0 } # demo set d [dict create a 1 b 2 c 3] puts "dict value = $d" if {[getDictItem $d a]} { puts "key 'a' exists in dict. a=$a" } if {![getDictItem $d z]} { puts "key 'z' not exists in dict." }
HaO 2011-05-04 PYK 2019-10-16: On clt , the question was asked how to transform a list in a canonical dictionary (e.g. removing duplicate keys and normalizing the representation of each key and value).
In the following dictionary, there are two entries named a and one entry named b, so it is not in canonical form:
% set l {a 1 b 2 a 3}
Methods to transform the list into a canonical dictionary:
% dict create {*}$l % dict merge $l $l a 3 b 2
In the following cases, dict replace and dict merge do do not return canonical dictionaries:
dict replace $l dict merge $1 a 1 b 2 a 3
To resume, no method was found to directly transform a list in a canonical dictionary. There is always a small "derivation".
Within the thread, it was proposed to define dict replace $l as such a function, which is quite similar to lrange $l 0 end, which forms a canonical list.
PYK 2019-10-16: In current versions of Tcl dict replace does indeed return a new dictionary, meaning that redundant keys have been discarded and that the string representation of each value has been normalized.
Functions, which do a canonicalization:
% dict for {k v} $l {puts -nonewline "$k $v "} ; puts "" a 3 b 2 % dict size $l 2
AMG: Actually, this is incredibly easy to do. Just call [dict get $dictValue] with no additional arguments.
% dict get {a 1 b 2 a 3} a 3 b 2
Taken from a posting on comp.lang.tcl , this is code for pretty-printing a dict:
namespace eval DictUnsupported { package require Tcl 8.6 ######################### ## dict format dict # # convert dictionary value dict into string # hereby insert newlines and spaces to make # a nicely formatted ascii output # The output is a valid dict and can be read/used # just like the original dict ############################# proc dict_format {dict} { dictformat_rec $dict "" "\t" } proc isdict {v} { string match "value is a dict *" [::tcl::unsupported::representation $v] } ## helper function - do the real work recursively # use accumulator for indentation proc dictformat_rec {dict indent indentstring} { # unpack this dimension dict for {key value} $dict { if {[isdict $value]} { append result "$indent[list $key]\n$indent\{\n" append result "[dictformat_rec $value "$indentstring$indent" $indentstring]\n" append result "$indent\}\n" } else { append result "$indent[list $key] [list $value]\n" } } return $result } namespace ensemble configure dict -map \ [linsert [namespace ensemble configure dict -map] end format [namespace current]::dict_format] }
PYK 2019-10-16: ycl dict pretty is another routine the pretty-prints a dictionary.
You wish to implement procedures, but not wishing to source..
# Create a dictionary and store the procedure dict set myProcDict display_hello "proc display_hello {} { puts \"Hello\" }" # Prepare a call back proc get_dict_callback {callback} { uplevel #0 $callback } # This will sequence the display_hello procedure get_dict_callback [dict get $myProcDict display_hello] % display_hello Hello