Alan Saldanha
Alan Saldanha

Reputation: 87

Obtaining array values in Tcl

I have tcl arrays having the names as 0,1,2,.. and the values contain meaningful information. I want to use a single command like [array names $my_array] to get the values of the array. Right now I only see this option,(lengthy but gets job done)

for {set index 0} {$index<[array size my_array]} {incr index} {
    lappend my_list_values $my_array($index)
}

Upvotes: 0

Views: 5021

Answers (2)

glenn jackman
glenn jackman

Reputation: 247210

Here's a proc implementing a foreach functionality for arrays. In Tcl 8.7 this will be builtin: array for

# A note on performance: we're not saving any time with this approach.
# This is essentially `foreach name [array names ary] {...}
# We are saving memory: iterating over the names versus extracting
# them all at the beginning.
#
proc array_foreach {vars arrayName body} {
    if {[llength $vars] != 2} {
        error {array foreach: "vars" must be a 2 element list}
    }
    lassign $vars keyVar valueVar

    # Using the complicated `upvar 1 $arrayName $arrayName` so that any
    # error messages propagate up with the user's array name
    upvar 1 $arrayName $arrayName \
            $keyVar    key \
            $valueVar  value

    set sid [array startsearch $arrayName]
    # If the array is modified while a search is ongoing, the searchID will
    # be invalidated: wrap the commands that use $sid in a try block.
    try {
        while {[array anymore $arrayName $sid]} {
            set key [array nextelement $arrayName $sid]
            set value [set "${arrayName}($key)"]
            uplevel 1 $body
        }
    } trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
        puts stderr [list $e]
        dict set e -errorinfo "detected attempt to add/delete array keys while iterating"
        return -options $e
    } finally {
        array donesearch $arrayName $sid
    }
    return
}

You can add this to the array ensemble:

set map [namespace ensemble configure array -map]
dict set map foreach ::array_foreach
namespace ensemble configure array -map $map

then

% array set a {foo bar baz qux}
% array foreach {key value} a {puts "key=$key, value=$value"}
key=foo, value=bar
key=baz, value=qux

Upvotes: 0

Shawn
Shawn

Reputation: 52644

You can use array get to fetch all the elements of the array, and a foreach loop that consumes multiple elements of its list in each iteration (Plus sorting by key to get a reproducible order):

% set foo(0) a
a
% set foo(1) b
b
% set foo(2) c
c
% foreach {key val} [lsort -integer -stride 2 [array get foo]] { lappend values $val }
% puts $values
a b c
% 

Or with array names:

foreach key [lsort -integer [array names foo]] { lappend values $foo($key) }

Upvotes: 1

Related Questions