Given Data
array set color {a red b green c blue}
set keylist [array names $color]
pointlist {{1 2 3 4} {5 6 7 8} {9 10 11 12} {13 14 15 16}}
I get a new list after running a procedure.
newlist {b b b b} # Note: length of newlist is same as length of pointlist
Question is : when i run a loop these values of b should be substituted, for example in this green.
for {set i 0} { $i < [llength $pointlist]} {incr i } {
lassign [lindex $pointlist $i] x1 y1 x2 y2
APICommand -points "$x1,$y1,$x2,$y2" -color $color($keylist)
}
Now this $color($keylist) is not getting me correct result and errors out saying :
can't read "color(red blue green)": no such element in array
Instead I want the first set of 4 points to get the green color which is the value of b. Similarly next set of 4 points from pointlist should also get green as its value is also b.
#So, after substitution it should look like APIcommand -points 1,2,3,4 -color green ..... so on and so forth
Note this will not always be b , it just happens to be in this case.
Need solution asap. Thanks in advance.
Sounds like you need $color([lindex $newlist $i])
When iterating across a list, the foreach command is useful. It can also be used to iterate over multiple corresponding lists. Assuming newlist holds the colors that correspond to the points, I might try something like (usual warnings about untested code):
foreach point $pointlist new $newlist {
APICommand -points [join $point ,] -color $color($new)
}
Related
I'm porting a program from C to TCL, and I'm trying to implement a data structure similar to an Array in C. The two main things I need it to do are
Be Ordered
Allow insertion into any index
Return the array from a procedure
I will know the size of the array before run time, and the size should not change throughout the program (so it is static). Are there any data structures that fit this bill?
I'm using TCL 8.6 if that matters
EDIT: I also need to be able to return the data structure from a function.
The corresponding data structure would be list. It meets all your requirements. If you want it to have a fixed size, you "pre-allocate" it like this:
set data [lrepeat 8 {}]
which creates eight empty compartments.
It is ordered, you can access every element by index (0-based), and you can pass its value into procedures/functions and also return it. You can traverse it with e.g. foreach and for, and there are a lot of list manipulating commands.
While list is the Tcl data container that corresponds most closely to a C array, one can use array or dict to simulate a fixed-size, direct-access, ordered container.
# allocation
set listdata [lrepeat 8 {}]
array set arraydata {0 {} 1 {} 2 {} 3 {} 4 {} 5 {} 6 {} 7 {}}
set dictdata [dict create 0 {} 1 {} 2 {} 3 {} 4 {} 5 {} 6 {} 7 {}]
# or
set dictdata {0 {} 1 {} 2 {} 3 {} 4 {} 5 {} 6 {} 7 {}}
# access (write, then read)
lset listdata 5 96
lindex $listdata 5 ;# -> 96
set arraydata(5) 96
set arraydata(5) ;# -> 96
dict set dictdata 5 96
dict get $dictdata 5 ;# -> 96
# return from procedure
# (not possible with arraydata, arrays are shared using either
# scope manipulation or namespaces)
return $listdata
return $dictdata
# traversal
for {set i 0} {$i < 8} {incr i} {puts [lindex $listdata $i]}
# or
foreach elem $listdata {puts $elem}
for {set i 0} {$i < 8} {incr i} {puts [lindex $arraydata($i)]}
dict for {idx val} $dictdata {puts $val}
Documentation: array, dict, for, foreach, incr, lindex, lrepeat, lset, puts, return, set
Either the tcl array (some examples) or the tcl dict command will suit your needs.
A and B are mask indices (row and column respectively) and C is an image and I want to note the color values stored in C for which the indices are stored in A and B. If A and B would be something like [1, 2, 3] and [20, 30, 40] so I would like to find C(1, 20, :), C(2, 30, :) and C(3, 40, :).
If I do D = C(A, B, :), I get an array of size 3x3x3 in this case, however I want an array of size 3x1x3. I know I am messing with the indexing, is there a simple way to do this without writing a loop?
Simply stating, is there a way to do the following without a loop:
for i = 1:10
D(i, :) = C(A(i), B(i), :)
end
You need to convert subindices to linear indices. You can use sub2ind for that:
r = C(sub2ind([size(C,1) size(C,2) 1],A,B,1*ones(1,length(A))));
g = C(sub2ind([size(C,1) size(C,2) 2],A,B,2*ones(1,length(A))));
b = C(sub2ind([size(C,1) size(C,2) 3],A,B,3*ones(1,length(A))));
The n x 1 x 3 result you want would be simply cat(3, r.',g.',b.').
Why not something like
C = C(A,B(i),:);
You could use a for statement to get the value of i or set it some other way.
It sounds like everything is working as it should. In your example you've indexed 9 elements of C using A and B. Then D is a 3x3x3 array with the dimensions corresponding to [row x col x color_mask(RGB)]. Why would the second dimension be reduced to 1 unless B only contained one value (signifying you only want to take elements from one column)? Of course A and B must not contain values higher than the number of rows and columns in C.
A = [1 2 3];
B = [20];
D = C(A,B,:);
size(D)
>> 3 1 3
EDIT: Ok, I see what you mean now. You want to specify N number of points using A(Nx1) and B(Nx1). Not NxN number of points which is what you are currently getting.
where is the difference in TCL TK with list and array ?
I created a list of 3 arrays.
like this one in a loop
set x($idx) 1
incr idx
and later i want to return the "ret" object
list set ret { $x $x2 $x3 }
and parse them again with
lassign $data x x2 x3
but this wont work... :(
could someone please help me again.. damn tcl tk... :D:D
correct me if im not right, its not possible to build a 2dim list or array ?
Your array is called x - you can refer to its elements by set x(1) , set x(2) etc. $x2 and $x3 have no meanings in this case.
If you want a 2 dimensional array, you can simulate it in TCL as follows:
set a(1,1) 0 ;# set element 1,1 to 0
set a(1,2) 5 ;# set element 1,2 to 5
It might be easier if you just use a list of lists
set l1 [list a b c]
set l2 [list d e f]
set lol [list $l1 $l2]
You can use array get/set to pass arrays as procedure arguments / return values. For example:
proc someProc {arr} {
array set x $arr
set x(5) 0
return [array get x]
}
Example of usage:
% set a(0) -1
% set a(1) 1
% parray a
a(0) = -1
a(1) = 1
% array set b [someProc [array get a]]
% parray b
b(0) = -1
b(1) = 1
b(5) = 0
The problem is that the order in which I "insert" elements in an array changes throughout the execution of the script.
Here is a quick reproduction of the problem:
#!/bin/bash
# : \
exec /home/binops/afse/eer/eer_SPI-7.3.1/tclsh "$0" "$#"
proc myProc { theArray } {
upvar $theArray theArrayInside
parray theArrayInside
puts "------"
foreach { key value } [array get theArrayInside] {
puts "$key => $value"
}
}
# MAIN
set myArray(AQHI) AQHI
set myArray(O3) 1
set myArray(NO2) 2
set myArray(PM2.5) 3
parray myArray
puts "------"
myProc myArray
Output is:
myArray(AQHI) = AQHI
myArray(NO2) = 2
myArray(O3) = 1
myArray(PM2.5) = 3
------
theArrayInside(AQHI) = AQHI
theArrayInside(NO2) = 2
theArrayInside(O3) = 1
theArrayInside(PM2.5) = 3
------
PM2.5 => 3
O3 => 1
NO2 => 2
AQHI => AQHI
Notice I didn't use generic keys like A, B, C and generic values like 1, 2, 3, as you may have expected. This is because the order isn't messed up with these generic keys/values. Maybe this can help identify the problem.
Also notice that the initial order (AQHI, O3, NO2, PM2.5) is lost even at the first call to parray (order is now AQHI, NO2, O3, PM2.5; alphabetically sorted?). It is then changed again upon calling array get ... (inversed?)
So anyways, the question is: How can I make sure the initial order is kept?
You're making the mistake of equating Tcl arrays to those in a language like C, where it's a list of elements. Instead, Tcl arrays are maps (from a key to a value) like a HashMap in Java, and the order of elements is not preserved.
You may be better off using a list (if you just have a number of items you need to store in order).
If you're using 8.5 or higher, a dict if you actually have a mapping of keys to values, since dictionaries are order preserving maps. There are dict backports to Tcl versions before 8.5, but I'm not sure whether they preserve order (and they're slower).
If you can't use 8.5 dicts and need key/value pairs, one option is to use a list of key value pairs and then use lsearch to pull out the values you need
> set mylist {{key1 value1} {key2 value2} {key3 value3}}
> lsearch -index 0 $mylist key2
0
> lindex $mylist [list [lsearch -index 0 $mylist key2] 1]
> value2
> proc kv_lookup {dictList key} {
set index [lsearch -index 0 $dictList $key]
if {$index < 0} {
error "Key '$key' not found in list $dictList"
}
return [lindex $dictList [list $index 1]]
}
> kv_lookup $mylist key2
value2
Man pages for 8.4 are here
You may also want to look at this page on keyed lists for Tcl. It implements what I mentioned above, plus some other useful commands.
For an example of the different between an ordered "map" and an unordered one, you can take a look at the two java classes HashMap (unordered) and LinkedHashMap (ordered).
Tcl is flexible enough that one can devise many schemes to handle what you want. Here's an idea that stores the order of your keys within the array itself, assuming the empty string is not a valid key in your data:
proc array_add {ary_name key value} {
upvar 1 $ary_name ary
set ary($key) $value
lappend ary() $key
}
proc array_foreach {var_name ary_name script} {
upvar 1 $var_name var
upvar 1 $ary_name ary
foreach var $ary() {
uplevel 1 $script
}
}
array_add a foo bar
array_add a baz qux
array_add a abc def
array_add a ghi jkl
array_foreach key a {puts "$key -> $a($key)"}
# foo -> bar
# baz -> qux
# abc -> def
# ghi -> jkl
array names a
# ghi {} foo baz abc
array get a
# ghi jkl {} {foo baz abc ghi} foo bar baz qux abc def
parray a
# a() = foo baz abc ghi
# a(abc) = def
# a(baz) = qux
# a(foo) = bar
# a(ghi) = jkl
I just found myself in a position where I have two arrays in Tcl.
I'm given $W_Array and $P_Array.
I need to traverse through one array not knowing what the size of each one is before hand, and execute a command only when there is a value for both arrays. Yes the array lengths could be different.
What is the best way of doing this?
The other answers jumped to using lists, I presume you mean Tcl's array, which are also called hash maps or associative arrays.
I think you're asking for something like:
array set a1 {a 1 b 2 c 3 d 4 e 5}
array set a2 {z 0 x 1 b 2 e 99}
foreach n [array names a1] {
if {[info exists a2($n)]} {
puts "Do something with $a1($n) and $a2($n)"
}
}
# FOREACH LOOP RESULTS IN THESE TWO PRINTOUTS
Do something with 5 and 99
Do something with 2 and 2
Not sure exactly what you mean by "a value for both arrays", but tcl's foreach supports iteration over multiple arrays at once... so you can say e.g.
foreach w $W_Array p $P_Array {
if {$w == $val && $p == $val} {
...
}
}
When the arrays are not of the same length, foreach will return all values from the longest array and the empty value {} for the missing elements in any shorter arrays.
Use llength command to find out if the arrays contain a value.
if {[llength $W_Array] > 0 && [llength $P_Array] > 0} {
# Do something
}