The following is a few extensions to the dict command which I have found useful. (The proc-ensemble command is the one shown on my fredderic's gems page.) Of the lot shown here, the two get variants are by far the most useful.
If key exists within the dictionary, then store the value in varName and return true. Otherwise, returns false and leaves the variable unchanged.
If key exists within the dictionary, then return it as usual. Otherwise, return the supplied default value.
Sets a new value in the dictionary, but returns any existing value in the manner of dict get? as well.
Sets a new value in the dictionary, but only if no value for that key already exists. (Not particularly useful, but it's had its moments.)
Unsets a key from the dictionary, but returns any existing value in the manner of dict get? as well.
Mostly for symmetry, this one simply doesn't error if the key does not exist.
Intended primarily for dictionary keys containing lists of flags, this command won't append the given value to a key which already contains it.
Sets the specified key / value pair over each of the sub-keys listed in itemList.
Merge several dictionaries, inserting an extra level of nesting to distinguish the values from each dictionary.
proc-ensemble ::dict {
get? {args} {
# dict::get to variable and return boolean
if { [llength $args] < 3 } {
error-args "dict get? dictionary key ?...? varName"
}
::set varName [lindex $args end]
::set args [lrange $args 0 end-1]
if { [llength [lindex $args 0]] % 2 == 1 } {
return -level 1 -code error \
"missing value to go with key"
}
if { [dict exists {*}$args] } {
uplevel 1 [list ::set $varName [dict get {*}$args]]
return 1
}
return 0
}
get! {args} {
# dict::get with default return value
if { [llength $args] < 3 } {
error-args "dict get! dictionary key ?...? default"
}
::set default [lindex $args end]
::set args [lrange $args 0 end-1]
if { [llength [lindex $args 0]] % 2 == 1 } {
return -level 1 -code error \
"missing value to go with key"
}
if { [dict exists {*}$args] } {
return [dict get {*}$args]
} else {
return $default
}
}
set? {args} {
# dict::set but return previous value
if { [llength $args] < 4 } {
error-args "dict set? varName key ?...? value varName"
}
upvar 1 [lindex $args 0] dict [lindex $args end] var
::set path [lrange $args 1 end-2]
::set valu [lindex $args end-1]
if { [::info exists dict] && [dict exists $dict {*}$path] } {
::set var [dict get $dict {*}$path]
::set rc 1
} else {
::set rc 0
}
dict ::set dict {*}$path $valu
return $rc
}
set! {args} {
# dict::set but only if element doesn't exist
if { [llength $args] < 3 } {
error-args "dict set! varName key ?...? value"
}
upvar 1 [lindex $args 0] var
::set args [lrange $args 1 end]
if { ! [::info exists var] ||
! [dict exists $var {*}[lrange $args 0 end-1]] } {
dict set var {*}$args
}
return $var
}
unset? {args} {
# dict::unset but returns boolean and old value
if { [llength $args] < 3 } {
error-args "dict unset? varName key ?...? retVar"
}
upvar 1 [lindex $args 0] var [lindex $args end] ret
::set args [lrange $args 1 end-1]
if { [::info exists var] && [dict exists $var {*}$args] } {
::set ret [dict get $var {*}$args]
dict unset var {*}$args
return 1
}
return 0
}
unset! {args} {
# dict::unset but doesn't throw error
if { [llength $args] < 2 } {
error-args "dict unset! varName key ?...?"
}
upvar 1 [lindex $args 0] var
::set args [lrange $args 1 end]
if { ! [::info exists var] || ! [dict exists $var {*}$args] } return
return [dict unset var {*}$args]
}
lappend! {args} {
# dict::append but only if list element doesn't exist
if { [llength $args] < 3 } {
error-args "dict lappend! varName key ?...? value"
}
upvar 1 [lindex $args 0] var
::set value [lindex $args end]
::set args [lrange $args 1 end-1]
if { [::info exists var] && [dict exists $var {*}$args] } {
::set list [dict get $var {*}$args]
if { $value in $list } {return $var}
lappend list $value
} else {
::set list [list $value]
}
return [dict set var {*}$args $list]
}
multiset {args} {
# dict::set that sets the same key/value over multiple sub-keys
if { [llength $args] < 4 } {
error-args "dict multiset dictionary ?key ...? itemList key value"
}
::set keys [lassign [lrange $args 0 end-3] dictionary]
lassign [lrange $args end-2 end] items key value
# now process the multiset
::set dict [dict get $dictionary {*}$keys]
foreach item $items {
dict set dict $item $key $value
}
if { [llength $keys] == 0 } {return $dict}
return [dict set dictionary {*}$keys $dict]
}
zip {args} {
# dict::merge that adds a level inbetween
if { [llength $args] % 2 == 1 } {
error-args "dict zip ?key dictionary? ..."
}
::set dict [list]
foreach {key items} $args {
dict for {name item} $items {
dict set dict $name $key $item
}
}
return $dict
}
}