Version 1 of Creating Dicts

Updated 2012-05-26 16:27:39 by pdt

I've been playing around a little with dictionary creation. The Tcl code below allows a dictionary, say this one:

    set person {
        name "John Smith"
        age 35
        address {
            address1 "30 New Road"
            address2 "Old Estate"
            town "Reading"
            county "Berkshire"
        }
        children {
            {
                {
                    name Janet
                    age 3
                }
                {
                    name Joe
                    age 5
                }
            }
        }
    }

to be created instead like this:

    set person [dict config {
        set name "John Smith"
        set age 35
        set address [dict config {
            set address1 "30 New Road"
            set address2 "Old Estate"
            set town "Reading"
            set county "Berkshire"
        }]
        set children [list]
        lappend children [dict config {
            set name Janet
            set age 3
        }] [dict config {
            set name Joe
            set age 5
        }]
    }]

Within the dict config code block, a key/value is only added to the dictionary by setting variables in the code block using set (not ::set). The dictionary is updated at the end of the code block.

Variables within the dict config code block can't be seen outside of that code block. Also, dict config either creates a new dictionary, or modifies an existing one, as shown in the test code below.


Here's the code:

namespace eval ::DictExtension {
    namespace export dict

    proc dict {args} {
        if {[lindex $args 0] eq "config"} {
            if {[llength $args] < 2} {
                error "dict config error"
            }
            ::set block [format {
                ::dict for {configVar configVal} $configVars {
                    set $configVar $configVal
                }
                %s
                foreach configVar [::dict keys $configVars] {
                    ::dict set configVars $configVar [set $configVar]
                }
                return $configVars
            } [lindex $args end]]
            if {[llength $args] == 2} {
                return [apply [list {configVars} $block ::DictExtension] ""]
            } else {
                if {![uplevel info exists [lindex $args 1]]} {
                    uplevel set [lindex $args 1] \{\}
                }
                if {[llength $args] == 3 || [uplevel ::dict exists \
                            \$[lrange $args 1 end-1]]} {
                    set vars [uplevel ::dict get \$[lrange $args 1 end-1]]
                } else {
                    set vars ""
                }
                set vars [apply [list {configVars} $block ::DictExtension] \
                        $vars]
                ::dict for {var val} $vars {
                    uplevel ::dict set [lrange $args 1 end-1] \{$var\} \{$val\}
                }
            }
        } else {
            return [uplevel ::dict $args]
        }
    }

    proc set {args} {
        if {[llength $args] == 2} {
            upvar configVars vars
            ::dict set vars [lindex $args 0] [lindex $args 1]
        }
        return [uplevel ::set $args]
    }
}

namespace eval test {
    namespace import ::DictExtension::*

    set person [dict config {
        set name "John Smith"
        set age 35
        set address [dict config {
            set address1 "30 New Road"
            set address2 "Old Estate"
            set town "Reading"
            set county "Berkshire"
        }]
        set children [list]
        lappend children [dict config {
            set name Janet
            set age 3
        }] [dict config {
            set name Joe
            set age 5
        }]
    }]
    puts "initial:\n$person"
    puts [dict get $person name]
    puts [dict get $person address address1]

    dict config person address {
        set address1 "New House"
        set address2 "Another Estate"
        set town "Milton Keynes"
        set county "Buckinghamshire"
    }
    puts "\nfirst update:\n$person"
    puts [dict get $person name]
    puts [dict get $person address address1]

    dict config person {
        incr age
        lappend children [dict config {
            set name Jack
            set age 0
        }]
    }
    puts "\nsecond update:\n$person"
    puts [dict get $person name]
    puts [dict get $person age]
}

And here's the output:

initial:
name {John Smith} age 35 address {address1 {30 New Road} address2 {Old Estate} town Reading county Berkshire} children {{name Janet age 3} {name Joe age 5}} 
John Smith
30 New Road

first update:
name {John Smith} age 35 address {address1 {New House} address2 {Another Estate} town {Milton Keynes} county Buckinghamshire} children {{name Janet age 3} {name Joe age 5}}
John Smith
New House

second update:
name {John Smith} age 36 address {address1 {New House} address2 {Another Estate} town {Milton Keynes} county Buckinghamshire} children {{name Janet age 3} {name Joe age 5} {name Jack age 0}}
John Smith
36