How do I get the hash name and key in TCL? - arrays

I'm trying to figure out how to get the hash name and key in the following situation. Say I have the following hash value:
set CLIENT(CAR) "Koenigsegg"
If I pass $CLIENT(CAR) into a proc, the value that is passed is "Koenigsegg". Is there a way to also capture the fact that the hash and key storing that value is 'CLIENT' and 'CAR', respectively?
For example:
proc foobar {item} {
set the_item $item
}
foobar $CLIENT(CAR)
In this example, the proc only receives the value of $CLIENT(CAR), which is "koenigsegg". The $item is "koenigsegg", but I don't know what kind of item it is. I'd like to get the hash name "CLIENT" and key "CAR" to know that "koenigsegg" it is a "client car".

You can pass the name of the array into a proc, then use upvar to access it:
proc process_array {arrayName} {
upvar 1 $arrayName myArray
puts "Car is $myArray(CAR)"
}
set CLIENT(CAR) "Koenigsegg"
process_array CLIENT ;# Pass the name of the array, note: no dollar sign
Output:
Car is Koenigsegg
I hope this is what you are looking for.
Update
So, you want to pass two things into the proc: the hash name (Tcl refers to it as "array") and the index name (CAR):
proc process_array {arrayName index} {
upvar 1 $arrayName myArray
puts "My array is $arrayName"
puts "List of indices: [array names myArray]"
puts "Car is $myArray($index)"
}
set CLIENT(CAR) "Koenigsegg"
process_array CLIENT CAR;# Pass the name of the array, note: no dollar sign
Output:
My array is CLIENT
List of indices: CAR
Car is Koenigsegg
Update 2
It seems that the original poster (OP) is asking for something like this:
process_array $CLIENT(CAR)
and expect the proc process_array to figure out the name of the array (CLIENT) and the index (CAR). This is not possible in my knowledge. When Tcl interpreter encounters the line above, it evaluates the $CLIENT(CAR) expression and the line becomes:
process_array Koenigsegg
That means within process_array, the proc has no idea about any array. All it knows is someone has passed to it a string "Koenigsegg".
Now, if you pass into the proc the name of the array, then it can figure out the array's name, any any indices the array has. Please see my previous code.

Related

Certain elements in tcl array not accessable (i.e. "no such element in array")

I have declared an array, of which I can access all of the elements through a loop but hen I try to directly access some elements, they do not exist.
I have tried accessing the array through CUI, writing scripts to do so, both in a loop, directly, and by making the key a variable; the behavior is very bizzare.
I have my array tileDirectory declared from before hand and when I enter in the CUI
foreach {a b} [array get tileDirectory] {puts $a}
…
xu/iq/grf/reg23DataReg/[0]
su/pipe/dcacheTag/dcPLRUEntry[31].dcPLRUEntryXReg/[0]
xu/cq/cq00/cqStatusReg/[0]
su/busReq/memIntfc/memReqStDataReg/[96]_nocgc
However if I do:
puts $tileDirectory(su/pipe/dcacheTag/dcPLRUEntry[31].dcPLRUEntryXReg/[0])
can't read "tileDirectory(su/pipe/dcacheTag/dcPLRUEntry[31].dcPLRUEntryXReg/[0])": no such element in array
I would except that I could read the element of the array, as a note the other elements I can read out just fine, and for the sake of pattern, it tends to be the names which have "." in them that give me trouble
Apparently, you have your array defined in multiple places (or in different namespaces) such as the sample program below. For the following program to work, the global td statement must be uncommented.
proc myproc { } {
# global td
set td(abc) 123
puts $td(def)
}
set td(def) 456
puts $td(def)
myproc

printing out the values of a hash coming from an array

I have a hash called sales_hash that I am printing out. Each hash has a key called sku that matches a hash inside the array of array_items. I get the hash out of the array and am trying to print the values of the hash based on the key which is :item but I keep getting an error. What am I doing wrong?
sales_hash.take(10).each do |a, b|
temp_hash = array_items.select{|array_items| array_items[:sku] == a}
puts temp_hash
puts "Sku is #{a} the amount sold is #{b} the name of the book is #{temp_hash[:price]}"
end
The line #{temp_hash[:item]}" keeps giving me an error
Your temp_hash is actually an array.
From the Docs:
select -> Returns a new array containing all elements of ary for which the given block returns a true value.
And you can't access and array like this: array[:key].
Since temp_hash is an array, and you're sure that there's only one item inside that array, the proper way to get the content of temp_hash is using "first" like this:
#{temp_hash.first[:price]}
As your temp_hash is an array, so you can access the expected hash like this:
temp_hash[0] # this will give you the expected hash data
And, then you can access the required key inside the hash (e.g. price):
temp_hash[0][:price]

accessing array result in procedure using tcl

am beginner in Tcl/tk, facing problem in accessing array in procedure
below is my problem statement
proc myproc {args} {
set aaa ""
set bbb ""
set ccc ""
foreach "field value" $args {
set $field $value
}
# assigning input args values to an array a
set a(3:0) $aaa
set a(6:4) $bbb
set a(25:7) $ccc
#do some computation on input arguments may be addition
#
#
#
# now the result am trying to fetch into another array b
set $b(word0) $x
set $b(word1) $y
set $b(word2) $z
set $b(word3) $u
return [array get b]
}
now i need to pass arguments to myproc and return array i need to access.
set args_1 "g 1 h 4 k 6"
i tried the below syntax it was throwing me error.
array set a [myproc[array get $args_1]]
can somebody help me in solving this issue
trying to give string as input for procedure myproc
and later trying to do some computation with that input values.
later after all computation got set of string values, which are assigned to array as below
set $b(word0) $x
set $b(word1) $y
set $b(word2) $z
set $b(word3) $u
want to send this array b as return.
example:
proc myproc {} {
set $b(word0) $x
set $b(word1) $y
set $b(word2) $z
set $b(word3) $u
return [array get b]
}
i have tried to access array b as below
array set a [myproc[array get b]]
it worked :) was able to creat new array in calling function.
but, need to pass string arguments to myproc and get return as array
That function looks OK to me. There may be better ways to write it but it's essentially OK as is.
There are however a couple of problems with how you call that function.
First, you're confusing arrays and lists. In tcl, an array is a collection of key-value pairs. Other languages call this a "hash" or "map". A list is what is sounds like: a list of values. Other languages call this "array" or "list".
So, first off:
tcl other languages
--- ---------------
array = hash
list = array
The name "array" was chosen because the concept of a collection of key-value pairs is known in computer science as "associative arrays". Which is a term that predates the use of the word "array" to mean a list of values in languages like C and Java.
So, here you're declaring a list:
set args_1 "g 1 h 4 k 6"
And you're trying to access it as an array:
array get $args_1
Which should throw out an error that says that $args_1 is not an array. Which is in fact true.
So, simply replace it with the list variable:
$args_1
Which gives us:
array set a [myproc$args_1]
This should throw another error which basically says that the function myproc g 1 h 4 k 6 doesn't exist. Yes, in tcl it is valid to have whitespace in a function's name. For example:
proc "an example" {} {return "an example"}
That's valid code. And you call it like this:
set x ["an example"]
So it's no surprise that tcl can't find a function called "myproc g 1 h 4 k 6".
What this means is that whitespace is significant in tcl. You can't do:
set x [y[z]]
That's most likely a syntax error. It should be:
set x [y [z]]
# ^
# |______ whitespace was missing
So your code should be:
array set a [myproc $args_1]

creating and appending an existing array in tcl

Hi all i need some help on tcl arrays. I have two procedures in tcl something like below :
proc A {} {
set lst_A [list];
if {true} {
lappend lst_A [::B $file1];
} else {
foreach id $allId {
lappend lst_A [::B $id];
}
}
return $lst_A;
}
proc B {fileID} {
set fileName [::getFileName $fileID]; # getFileName returns filename from fileid
set tcName "[set ItemName]_[set ItemId]";
array set tcArrayName {$fileName $tcSpec};
return $tcArrayName;
}
Now i want to create an array which will be a key-value pair where key is some kind of file id and value is some name associated with that id. Now the thing is in proc A if condition is true i want to create array with one key-value pair only and then append that array to lst_A which in this case will contain only one item i.e. the array returned. But if the condition is false than i loop through some ids and for each id i call proc B and create array which is then appended to lst_A which in this case will contain multiple key-value paired arrays.
So I wrote the above two procedures and created array after reading about arrays in tcl tutorial. But not sure if this is correct way or the most optimised way.
My ultimate goal is to create a lst_A or i would say should be an array which will either contain just one key-value pair if condition is true or else will be an array with multiple key-value pairs. Since i am creating the array in proc B, i could only think of returning the key-value pair from proc B as an array and then append that array to a list i.e. lst_A in proc A.
Any suggestions???
You're confusing array's with lists here.
In tcl, you cannot pass arrays into or return them from functions. Also, tcl uses the term "arrays" for what C++ calls "maps" or Perl and Ruby call "hashes" or what Javascript calls "objects". The term comes from the fact that all these things are generically known as "associative arrays" in computer science. So "arrays" in tcl are key-value pairs, not sequences of data.
Arrays don't normally degenerate into values (or what a seasoned tcl programmer would call "strings"). Therefore this:
array set tcArrayName {$fileName $tcSpec};
return $tcArrayName;
generates a syntax error:
can't read "tcArrayName": variable is array
You can actually degenerate the contents of an array into a value that you can then return. But you have to do it manually via the [array get] command:
return [array get tcArrayName]
The above command will return the content of the tcArrayName array in the form of a two element list:
"$fileName $tcSpec"
which also happens to be a string. And that's the actual string: the character '$' followed by the characters 'f', 'i', 'l', 'e', 'N', 'a', 'm', 'e' etc. Not the filename and the concatenated item name and item id. The literal string "$fileName $tcSpec".
That's because you've used the {} grouping in this line of code:
array set tcArrayName {$fileName $tcSpec}
In tcl, {xxx} behaves the same way 'xxx' does in Perl. Basically its a literal string that does not get substituted. If you want tcl to do $ substitution then you need to use "" grouping:
array set tcArrayName "$fileName $tcSpec"
But this is fragile because if fileName contains spaces then it would break the code. A more robust way to do this is:
array set tcArrayName [list $fileName $tcSpec]
But using an array in this case is a bit redundant since all you're doing is initialising it with a key-value list (also known as a dict in more modern versions of tcl. BTW, what version of tcl are you using?) and then immediately discarding it and returning it's content as a key-value list.
Why not simply return that key-value list:
proc B {fileID} {
set fileName [::getFileName $fileID]; # getFileName returns filename from fileid
set tcName "[set ItemName]_[set ItemId]"
return [list $fileName $tcSpec]
}
But we're still not in the clear. If you try to execute B you'll see this error:
can't read "ItemName": no such variable
That's because tcl does not import global variables into functions by default. As such the variables ItemName and ItemId don't exist in B. You either need to import it explicitly via the global command:
proc B {fileID} {
global ItemName
global ItemId
# ...
}
or access it via the global namespace using a fully qualified variable name:
set tcName "[set ::ItemName]_[set ::ItemId]"
So B should look like this:
proc B {fileID} {
set fileName [::getFileName $fileID]; # getFileName returns filename from fileid
set tcName "[set ::ItemName]_[set ::ItemId]"
return [list $fileName $tcSpec]
}
That's B taken care of but A has some problems as well:
can't read "file1": no such variable
We need to modify it a bit to access the variable file1:
proc A {} {
set lst_A [list];
if {true} { ;# assuming that there will be a valid condition here
lappend lst_A [::B $::file1];
} else {
foreach id $allId {
lappend lst_A [::B $id];
}
}
return $lst_A;
}
This should work as you expect.
If you want a little speed up when you use the key-value list then you should really read up on dicts. As is, dicts won't affect the code given above but it does affect the code calling A. If you plan on using dicts then you can replace the lappend call above with dict append. But older versions of tcl don't have dicts.

TCL: how to return an array?

please find below a snippet of code that passes an array, manipulates the array, but i cannot return the new version of array.
here is the snippet :
proc get_mroute_active { &multicast } {
upvar ${&multicast} MULTICAST ;
set group -1 ;
set src -1 ;
set mcast_group_source_id -1 ;
set MULTICAST($mcast_group_source_id,id) $mcast_group_source_id ;
set MULTICAST($mcast_group_source_id,mcast_group) $group ;
set MULTICAST($mcast_group_source_id,mcast_source) $src ;
puts [array size MULTICAST] ;
parray MULTICAST ;
}
array set multicast { } ;
get_mroute_active [array get multicast] ;
puts [array size multicast] ;
parray multicast ;
And the output of the code is :
3
MULTICAST(-1,id) = -1
MULTICAST(-1,mcast_group) = -1
MULTICAST(-1,mcast_source) = -1
0
Could you please help show me how the "MULTICAST" variable can be assigned to "multicast" ?
The short answer is: you can't return an array from a procedure as arrays are not values — they are peculiar named collections of named values.
There are several ways to deal with this:
The usual approach is to pass arrays by names and make the called procedure modify the array in place. For instance, the code
proc foo {arrayName} {
upvar 1 $arrayName ary
incr ary(one)
}
set x(one) 1
foo x
puts $x(one)
will print "2" as the procedure foo modified a specific value in the specified array in the caller's scope.
Notice how the caller passed the name of an array, "x", instead of "its value" (as you cannot extract a value from an array; but see below) and then the called procedure bound a local variable to that array by its name using the upvar command.
The other approach is to employ array get and array set commands to extract keys and values from arrays and populate arrays with keys and values, respectively. For instance:
set x(one) 1
set x(two) 2
array set another [array get x]
parray another
would print
another(one) = 1
another(two) = 2
The array get command, given an array, returns a flat list with its keys and their respective values interleaved. This way you can return the contents of an array from a procedure and then make the caller do whatever it wishes with these contents, for instance, use the array set command to populate another array in its scope (possibly the same which has been passed to that procedure in the first place).
Note that array set has merge semantics: it does not empty the target array before inserting/replacing its values using the source list.
The third (and may be the best) approach is to use dictionaries which are key/value maps as arrays do but are themselves values, so they can be passed around freely as other values. This requires Tcl 8.5 or later (in Tcl 8.4, you can use a backport of this code in the form of a package. With dicts you get it like this:
proc foo d {
dict set d one 2
return $d
}
set x [dict create]
dict set x one 1
set y [foo $x]
puts $y
which would print "one 2" as the procedure modified the original dictionary then returned it and then the caller assigned it to another variable.

Resources