CINXE.COM
dictn
<!DOCTYPE html> <html lang='en'> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width, initial-scale=1'> <meta name='description' content='Tclers wiki'> <meta name='author' content=''> <link rel='icon' href='/img/favicon.ico'> <title>dictn</title> <!-- Latest compiled and minified CSS --> <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'> <link rel='stylesheet' href='/css/nikit.css' type='text/css'> <link rel='stylesheet' href='/css/sh_style.css' type='text/css'> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.0/css/theme.bootstrap_3.min.css' type='text/css'> <script src='//cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js'></script> </head> <body onload='sh_highlightDocument(); nikitUser();'> <nav class='navbar navbar-fixed-top navbar-inverse'> <div class='container'> <div class='navbar-header'> <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='#myNavbar'> <span class='icon-bar'></span> <span class='icon-bar'></span> <span class='icon-bar'></span> </button> <ul class='nav navbar-nav'> <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'> Tcler's Wiki<span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a href='/welcome'>Home</a></li> <li><a rel='nofollow' href='/recent'>Changes</a></li> <li><a rel='nofollow' href='/_random'>Random page</a></li> <li><a rel='nofollow' href='/_new'>New page</a></li> </ul> </li> </ul> </div> <div class='collapse navbar-collapse' id='myNavbar'> <ul class='nav navbar-nav'> <li class='dropdown' id='li_idPageEdit' style='display:none'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'><span id=name_idPageEdit>Page</span><span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a rel='nofollow' href='/_edit/dictn?A=1'>Comment</a></li> <li><a rel='nofollow' hidden='true' href='/_edit/dictn'>Edit</a></li> <li><a rel='nofollow' href='/_upload/dictn'>Upload</a></li> <li><a rel='nofollow' href='/ref/dictn'>References</a></li> <li><a rel='nofollow' href='/history/dictn'>History</a></li> </ul> </li> <li class='dropdown' id='li_idPageNoEdit' style='display:none'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'><span id=name_idPageNoEdit>Page</span><span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a rel='nofollow' href='/ref/dictn'>References</a></li> <li><a rel='nofollow' href='/history/dictn'>History</a></li> </ul> </li> <li><a href="/page/Showcase">Showcase</a></li> <li><a href="/page/Tcl+Tutorial+Lesson+0">Tutorial</a></li> <li><a href="/page/Articles">Articles</a></li> <li><a href="/page/Tcl+Playground">Playground</a></li> <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'> Help<span class='caret'></span></a> <ul class='dropdown-menu scrollable-menu' role='menu'><li><a rel='nofollow' href='/page/Help'>Page Markup</a></li> <li><a rel='nofollow' href='/page/How+do+Wiki+Categories+work'>Wiki Categories</a></li> <li><a rel='nofollow' href='/page/Contents'>Topics</a></li> <li><a rel='nofollow' target='_blank' href='https://chiselapp.com/user/stevel/repository/nikit/ticket'>Report Problems</a></li> <li><a rel='nofollow' href='/privacy'>Privacy</a></li> <li><a rel='nofollow' href='/license'>License</a></li> </ul> </li> </ul> <ul class='nav navbar-nav navbar-right'> <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href='#'><span id=name_SMenu>User</span><span class='caret'></span></a> <ul class='dropdown-menu' id='ul_SMenu'> </ul> </li> </ul> <form class='navbar-form navbar-right' method='post' action='/search' id='searchform'> <input name='Q' type='text' class='form-control' placeholder='Search...'/> <input type="hidden" name="sites" value="wiki.tcl-lang.org"/> </form> </div> </div> </nav> <div class='container'> <div class='row'> <div class='col-xs-12'> <h2>dictn</h2> </div> </div> <div class='row'> <div class='col-xs-12'> <p class='mkup_p'><a class='mkup_a mkup_known' href='/page/JMN'>JMN</a> 2006-10-21 'dictn' An experimental wrapper over dict - focused on nested dicts.</p><p class='mkup_p'><a class='mkup_a mkup_unknown' href='/_new?newpagename=JEC'>JEC</a> 2010-6-17 The following is nice, but is there an 8.4 version available? I'm use the tclDict package <a rel='nofollow' class='mkup_a' href='https://wiki.tcl-lang.org/5042'>https://wiki.tcl-lang.org/5042 <span class='glyphicon glyphicon-globe' aria-hidden='true'></span></a> </p><hr class='mkup_hr'><p class='mkup_p'>The dictn package implements the same subcommands as dict, except wherever the dict function takes a 'key', dictn takes a list which is a 'path'.</p><p class='mkup_p'>For some subcommands such as 'replace' and 'remove' this allows operations at different nesting levels in a single call, without having to explicitly extract,update,repack.</p><p class='mkup_p'>As datastructures such as dict will presumably be used extensively and in code hotspots such as inner loops - the performance hit of a wrapper such as this may be an issue.</p><p class='mkup_p'>'dictn merge' not implemented. Presumably it would merge based on the longest key paths.. It looked a little tricky to implement.</p><p class='mkup_p'>e.g</p><pre class='mkup_pre'> %package require dictn 0.1 %set data [dictn create {slot1 item1} {a 0 b 1} {slot1 item2} {a 100 b 101} {slot2 item1 a} 3] slot2 {item1 {a 3}} slot1 {item1 {a 0 b 1} item2 {a 100 b 101}} %dictn replace $data slot2 was-slot-2-data {slot1 item1 a} AAA slot2 was-slot-2-data slot1 {item1 {a AAA b 1} item2 {a 100 b 101} %dictn incr data {slot1 item1 b} 100 slot2 {item1 {a 3}} slot1 {item1 {a 0 b 101} item2 {a 100 b 101}} </pre><p class='mkup_p'>'dictn with' has an extra option to allow supply of the name of an array variable in which array keys are mapped to dict keys at the specified path. This is an alternative to auto-creating various variables (corresponding to possibly arbitrary dict keys) in the calling context.</p><p class='mkup_p'>e.g</p><pre class='mkup_pre'> %dictn with data {slot1 item1} info {parray info} info(a) = 0 info(b) = 101</pre><p class='mkup_p'>Unsetting or changing an array element value will be reflected in the dict. Adding new elements to the array will not add them to the dict. The array is also not cleared prior to the call. I don't know if that's ideal - just the way it's done for this version. sqlite does something similar with it's 'db eval $sql arrayname $script' Perhaps here it would make more sense to automatically clear the array, and reflect any new values into the dict. (?)</p> <p class='mkup_p'>It seems to me this wrapper can be used along side standard 'dict' calls on the same variable. So you could generally stick to standard dict, and just call this to avoid some extract,modify,repack situations. I'm assuming that using dictn like this isn't going to cause any shimmering/underlying rep problems - but I'll leave that for someone else to analyse at this stage.</p> <hr class='mkup_hr'><p class='mkup_p'>Save as dictn-0.1.1.tm and place on the Tcl8.5+ module path.</p><div class='sh_sourceCode'><button class='copybtn btn pull-right' data-clipboard-target='#mkup_code_0' title='Click to copy code snippet to clipboard'><span class='glyphicon glyphicon-copy' aria-hidden='true'></span></button><pre id='mkup_code_0' class='sh_tcl sh_sourceCode'> #Julian Noble 2006 # - Experimental wrapper around 'dict' to provide consistent syntax for nested operations. # - Lic: Freely distributable - same conditions as Tcl. # # package provide dictn [namespace eval ::dictn { variable version namespace export {[a-z]*} namespace ensemble create set version 0.1.1 }] ## ::dictn::append #This can of course 'ruin' a nested dict if applied to the wrong element # - i.e using the string op 'append' on an element that is itself a nested dict is analogous to the standard Tcl : # %set list {a b {c d}} # %append list x # a b {c d}x # IOW - don't do that unless you really know that's what you want. # proc ::dictn::append {dictvar path {value {}}} { if {[llength $path] == 1} { uplevel 1 [list dict append $dictvar $path $value] } else { upvar 1 $dictvar dvar ::set str [dict get $dvar {*}$path] append str $val dict set dvar {*}$path $str } } proc ::dictn::create {args} { ::set data {} foreach {path val} $args { dict set data {*}$path $val } return $data } proc ::dictn::exists {dictval path} { return [dict exists $dictval {*}$path] } proc ::dictn::filter {dictval path filterType args} { ::set sub [dict get $dictval {*}$path] dict filter $sub $filterType {*}$args } proc ::dictn::for {keyvalvars dictval path body} { ::set sub [dict get $dictval {*}$path] dict for $keyvalvars $sub $body } proc ::dictn::get {dictval {path {}}} { return [dict get $dictval {*}$path] } proc ::dictn::getdef {dictval path default} { return [dict getdef $dictval {*}$path $default] } proc ::dictn::getwithdefault {dictval path default} { return [dict getdef $dictval {*}$path $default] } proc ::dictn::incr {dictvar path {increment {}} } { if {$increment eq ""} { ::set increment 1 } if {[llength $path] == 1} { uplevel 1 [list dict incr $dictvar $path $increment] } else { upvar 1 $dictvar dvar if {![::info exists dvar]} { dict set dvar {*}$path $increment } else { if {![dict exists $dvar {*}$path]} { ::set val 0 } else { ::set val [dict get $dvar {*}$path] } ::set newval [expr {$val + $increment}] dict set dvar {*}$path $newval } return $dvar } } proc ::dictn::info {dictval {path {}}} { if {![string length $path]} { return [dict info $dictval] } else { ::set sub [dict get $dictval {*}$path] return [dict info $sub] } } proc ::dictn::keys {dictval {path {}} {glob {}}} { ::set sub [dict get $dictval {*}$path] if {[string length $glob]} { return [dict keys $sub $glob] } else { return [dict keys $sub] } } proc ::dictn::lappend {dictvar path args} { if {[llength $path] == 1} { uplevel 1 [list dict lappend $dictvar $path {*}$args] } else { upvar 1 $dictvar dvar ::set list [dict get $dvar {*}$path] ::lappend list {*}$args dict set dvar {*}$path $list } } proc ::dictn::merge {args} { error "nested merge not yet supported" } #dictn remove dictionaryValue ?path ...? proc ::dictn::remove {dictval args} { ::set basic [list] ;#buffer basic (1element path) removals to do in a single call. foreach path $args { if {[llength $path] == 1} { ::lappend basic $path } else { #extract,modify,replace ::set subpath [lrange $path 0 end-1] ::set sub [dict get $dictval {*}$subpath] ::set sub [dict remove $sub [lindex $path end]] dict set dictval {*}$subpath $sub } } if {[llength $basic]} { return [dict remove $dictval {*}$basic] } else { return $dictval } } proc ::dictn::replace {dictval args} { ::set basic [list] ;#buffer basic (1element path) replacements to do in a single call. foreach {path val} $args { if {[llength $path] == 1} { ::lappend basic $path $val } else { #extract,modify,replace ::set subpath [lrange $path 0 end-1] ::set sub [dict get $dictval {*}$subpath] ::set sub [dict replace $sub [lindex $path end] $val] dict set dictval {*}$subpath $sub } } if {[llength $basic]} { return [dict replace $dictval {*}$basic] } else { return $dictval } } proc ::dictn::set {dictvar path newval} { upvar 1 $dictvar dvar return [dict set dvar {*}$path $newval] } proc ::dictn::size {dictval {path {}}} { return [dict size [dict get $dictval {*}$path]] } proc ::dictn::unset {dictvar path} { upvar 1 $dictvar dvar return [dict unset dvar {*}$path } proc ::dictn::update {dictvar args} { ::set body [lindex $args end] ::set maplist [lrange $args 0 end-1] upvar 1 $dictvar dvar foreach {path var} $maplist { if {[dict exists $dvar {*}$path]} { uplevel 1 [list set $var [dict get $dvar $path]] } } catch {uplevel 1 $body} result foreach {path var} $maplist { if {[dict exists $dvar {*}$path]} { upvar 1 $var $var if {![::info exists $var]} { uplevel 1 [list dict unset $dictvar {*}$path] } else { uplevel 1 [list dict set $dictvar {*}$path [::set $var]] } } } return $result } proc ::dictn::values {dictval {path {}} {glob {}}} { ::set sub [dict get $dictval {*}$path] if {[string length $glob]} { return [dict values $sub $glob] } else { return [dict values $sub] } } # Standard form: #'dictn with dictVariable path body' # # Extended form: #'dictn with dictVariable path arrayVariable body' # proc ::dictn::with {dictvar path args} { if {[llength $args] == 1} { ::set body [lindex $args 0] return [uplevel 1 [list dict with $dictvar {*}$path $body]] } else { upvar 1 $dictvar dvar ::lassign $args arrayname body upvar 1 $arrayname arr array set arr [dict get $dvar {*}$path] ::set prevkeys [array names arr] catch {uplevel 1 $body} result foreach k $prevkeys { if {![::info exists arr($k)]} { dict unset $dvar {*}$path $k } } foreach k [array names arr] { dict set $dvar {*}$path $k $arr($k) } return $result } }</pre></div><hr><div class='mkup_centered'><table class='mkup_categories'><tr><td class='mkup_td'><a class='mkup_a' href='/page/Category+Package'>Category Package</a></td><td class='mkup_td'><a class='mkup_a' href='/page/Category+Data+Structure'>Category Data Structure</a></td></tr></table></div> </div> </div> <div class='row'> <div class='col-xs-12'> <div class='Footer'>Updated 2025-04-05 05:10:05</div> </div> </div> </div> <!-- jQuery library --> <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.0/js/jquery.tablesorter.combined.js'></script> <!-- Latest compiled JavaScript --> <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script> <script type='text/javascript' src='/scripts/nikit.js'></script> <script type='text/javascript' src='/scripts/sh_main.js'></script> <script type='text/javascript' src='/scripts/sh_tcl.js'></script> <script type='text/javascript' src='/scripts/sh_c.js'></script> <script type='text/javascript' src='/scripts/sh_cpp.js'></script> <!-- <script src='https://www.google.com/recaptcha/api.js'></script> --> <script src='https://hcaptcha.com/1/api.js'></script> <script>var clipboard = new ClipboardJS('.copybtn', { text: function(trigger) { return document.querySelector(trigger.getAttribute('data-clipboard-target')).textContent + '\n'; } }); sort_tables(); </script> </body> </html>