Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Access to list/table

65 views
Skip to first unread message

luc.de...@gmail.com

unread,
Mar 26, 2018, 5:08:10 PM3/26/18
to
Hi everybody,

I'm new to TCL, if it's not the right place to ask the question, please let me know.

I've a string, that I want to access like a table, list or assiociative array, but I'm a little bit lost...

If I print my table :

puts "table $mytable"

It print :

table {{
name lala
nodeaddr 8.5.2.1
port 80
}} {{
name lolo
nodeaddr 5.2.1.4
port 80
}}

If I try to access each value :

foreach item $bigstring {
puts "item $item"
}

It print :

item {
name lala
nodeaddr 8.5.2.1
port 80
}
item {
name lolo
nodeaddr 5.2.1.4
port 80
}

And then I can't access the value for each item. After reading the doc, it looks like multidimensional table dosn't exist, I'm wrong somewhere...

How can I do something like :

foreach item $bigstring {
puts "name = item(name)"
}

Thanks for your time.

Rich

unread,
Mar 26, 2018, 5:30:57 PM3/26/18
to
luc.de...@gmail.com wrote:
> Hi everybody,
>
> I'm new to TCL, if it's not the right place to ask the question,
> please let me know.

This is the right place for these kinds of questions.

Although you also did not tell us which version of Tcl you are using,
so my answers below won't work unless you are at least using version 8.5.

[this quote is a bit out of order....]

> After reading the doc, it looks like multidimensional table dosn't
> exist, I'm wrong somewhere...

They do exist. They just are not called 'arrays'. They are called
"nested lists":

# multi-dimensional array (3x3)
set arr [list [list 1 2 3] \
[list 4 5 6] \
[list 7 8 9]]

puts "Item at 1,1 is [lindex $arr 1 1]"

Running this you get:

Item at 1,1 is 5



> I've a string, that I want to access like a table, list or
> assiociative array, but I'm a little bit lost...
>
> If I print my table :
>
> puts "table $mytable"
>
> It print :
>
> table {{
> name lala
> nodeaddr 8.5.2.1
> port 80
> }} {{
> name lolo
> nodeaddr 5.2.1.4
> port 80
> }}

Ok, first, drop the doubled curly brackets, you don't really need
those, this works just fine too:

set mytable "{
name lala
nodeaddr 8.5.2.1
port 80
} {
name lolo
nodeaddr 5.2.1.4
port 80
}"

Now, what you have there is a string that can be accessed as a list
(note, while this does work, if you get that string from some other
source, you need to be sure they always properly format it as a valid
string representation of a Tcl list, otherwise you'll get weird data
dependent errors that seem to occur randomly.

What you have here is a list with two outer elements.

> If I try to access each value :
>
> foreach item $bigstring {
> puts "item $item"
> }

This iterates over the outer elements (note that foreach only "unwraps"
one layer of a nested list, the current top most one for whatever
string you give it). You then want to further look inside the things
you get back from foreach (but all you do below is "puts" them, so you
are partially on the right track, you just didn't go far enough.

>
> It print :
>
> item {
> name lala
> nodeaddr 8.5.2.1
> port 80
> }
> item {
> name lolo
> nodeaddr 5.2.1.4
> port 80
> }

In your list, your two elements are formatted as Tcl 'dicts' (again,
same warning for data dependent errors applies here). You just need to
go one level lower and "unwrap" the dicts you receive from foreach

> How can I do something like :
>
> foreach item $bigstring {
> puts "name = item(name)"
> }

Like this:

foreach item $mytable {
dict for {key value} $item {
puts "$key=$value"
}
}

If you combine the above with my "set mytable" a bit earlier, and run
it, you'll get this:

name=lala
nodeaddr=8.5.2.1
port=80
name=lolo
nodeaddr=5.2.1.4
port=80

luc.de...@gmail.com

unread,
Mar 26, 2018, 6:21:37 PM3/26/18
to
> This is the right place for these kinds of questions.
Great !
> Although you also did not tell us which version of Tcl you are using,
> so my answers below won't work unless you are at least using version 8.5.
8.4.6, I can't update, it's a locked system sadly...
> [this quote is a bit out of order....]
>
> > After reading the doc, it looks like multidimensional table dosn't
> > exist, I'm wrong somewhere...
>
> They do exist. They just are not called 'arrays'. They are called
> "nested lists":
Thanks a lot, I'm digging this.

> > table {{
> > name lala
> > nodeaddr 8.5.2.1
> > port 80
> > }} {{
> > name lolo
> > nodeaddr 5.2.1.4
> > port 80
> > }}
>
> Ok, first, drop the doubled curly brackets, you don't really need
> those, this works just fine too:
>
> set mytable "{
> name lala
> nodeaddr 8.5.2.1
> port 80
> } {
> name lolo
> nodeaddr 5.2.1.4
> port 80
> }"
>
The problem is I don't produce this string, I try to process the output of a tcl server-side application.

I've find a quick solution to remove the curly brackets :

% set tmpstring [string map {\{\{ \{} $mytable]
% set mytable [string map {\}\} \}} $tmpstring]

> Now, what you have there is a string that can be accessed as a list
> (note, while this does work, if you get that string from some other
> source, you need to be sure they always properly format it as a valid
> string representation of a Tcl list, otherwise you'll get weird data
> dependent errors that seem to occur randomly.

It should not happen, the form is very restrictive, I think I'll not receive not well formatted data. I'll check this, thanks for the advice.
Great, it works on my computer, but my computer run 8.6 and the server run 8.4.6, and I've a doubt about the presence of dict in it.

Thanks a lot for your reply, it give me a lots path to test !

luc.de...@gmail.com

unread,
Mar 26, 2018, 7:03:50 PM3/26/18
to
If the version I have to use doesn't have dict, I've tested an ugly code, for name only but this is the idea :

set tmpstring [string map {\{\{ \{} $mytable]
set mytable [string map {\}\} \}} $tmpstring]
foreach item $mytable {
set nextisname 0
set tmpitem [string trim $item]
foreach line [split $tmpitem] {
if { $line eq "name" } {
set nextisname 1
} else {
if { $nextisname eq 1 } {
puts "name $line"
set nextisname 0
}
}
}
}

I hope I can use your solution, because mine is ugly, at least performance is not an issue, it'll be run maybe 1 or two time per week.

Rich

unread,
Mar 26, 2018, 9:35:35 PM3/26/18
to
If you need to run your code that consumes this data on 8.4 (you should
really consider upgrading, 8.4 is anchient now) then you likely want
this:

forward-compatible dict
http://wiki.tcl.tk/10609

Rich

unread,
Mar 26, 2018, 9:39:42 PM3/26/18
to
luc.de...@gmail.com wrote:
>> > table {{
>> > name lala
>> > nodeaddr 8.5.2.1
>> > port 80
>> > }} {{
>> > name lolo
>> > nodeaddr 5.2.1.4
>> > port 80
>> > }}
>>
>> Ok, first, drop the doubled curly brackets, you don't really need
>> those, this works just fine too:
>>
>> set mytable "{
>> name lala
>> nodeaddr 8.5.2.1
>> port 80
>> } {
>> name lolo
>> nodeaddr 5.2.1.4
>> port 80
>> }"
>>
> The problem is I don't produce this string, I try to process the
> output of a tcl server-side application.

Ok, that is good news, in that you can likely rely on the server code
to produce Tcl compatible strings of list items (presuming the server
code builds up the string using the list command and friends).

> I've find a quick solution to remove the curly brackets :
>
> % set tmpstring [string map {\{\{ \{} $mytable]
> % set mytable [string map {\}\} \}} $tmpstring]

That works, but string map can do multiple replacements at once, so you
can do everything in one pass:

set mytable [string map [list \{\{ \{ \}\} \}] $mytable]

> Great, it works on my computer, but my computer run 8.6 and the
> server run 8.4.6, and I've a doubt about the presence of dict in it.

The code you are writing to consume this, where does it run? If it
also runs on 8.4, then see my other reply about the 'forward-compatible
dict' that is on the wiki.

Also, again, I highly suggest upgrading to a more modern Tcl if at all
possible.

Ralf Fassel

unread,
Mar 27, 2018, 5:07:07 AM3/27/18
to
* Rich <ri...@example.invalid>
| > I've find a quick solution to remove the curly brackets :
| >
| > % set tmpstring [string map {\{\{ \{} $mytable]
| > % set mytable [string map {\}\} \}} $tmpstring]
>
| That works, but string map can do multiple replacements at once, so you
| can do everything in one pass:
>
| set mytable [string map [list \{\{ \{ \}\} \}] $mytable]

Not sure whether this string-mapping introduces another hard-to-find
data-depended bug when the data themselves contain {{ or }} (yes, I
suspect TCL would quote them then in some fashion, 'BUT'...)

If the sending side is TCL, I'd rather rely on properly formatted list
elements:

set mytable {{{
name lala
nodeaddr 8.5.2.1
port 80
}} {{
name lolo
nodeaddr 5.2.1.4
port 80
}}}

foreach elt $mytable {
foreach {key value} [lindex $elt 0] {
puts "$key = $value"
}
}

(assuming the double {{ }} is actually produced on the sending side and
not some side-effect of how 'mytable' is created on the receiving side).

Just my EUR 0.01...
R'

Rich

unread,
Mar 27, 2018, 6:03:47 AM3/27/18
to
Ralf Fassel <ral...@gmx.de> wrote:
>>
> | set mytable [string map [list \{\{ \{ \}\} \}] $mytable]
>
> Not sure whether this string-mapping introduces another hard-to-find
> data-depended bug when the data themselves contain {{ or }} (yes, I
> suspect TCL would quote them then in some fashion, 'BUT'...)

That is possible as well.

>
> (assuming the double {{ }} is actually produced on the sending side and
> not some side-effect of how 'mytable' is created on the receiving side).

That is the thing, we have only seen what he OP has posted here, which
may have had some extra curly brackets added on the receiving side.

OP, any chance to show the raw output by the sending side?

luc.de...@gmail.com

unread,
Mar 27, 2018, 8:51:59 AM3/27/18
to
I really like to update, but it's not possible, I'm going through a F5 appliance, and you can't (AFAIK) update the TCL version.


https://support.f5.com/csp/article/K6091 , as you have to log, this is basically what is written :

The BIG-IP iRules command set was developed from Tcl version 8.4.6.

https://support.f5.com/csp/article/K15909
Also, there is the list of commands removed :
auto_execok
auto_import
auto_load
auto_mkindex
auto_mkindex_old
auto_qualify
auto_reset
bgerror
cd
dict
eof
exec
exit
fblocked
fconfigure
fcopy
file
filename
flush
gets
glob
http
interp
load
lrepeat
lreverse
memory
namespace
open
package
pid
pkg::create
proc (enabled in version 11.4.0 and later)
pwd
rename
seek
socket
source
tcl_findLibrary
tell
time
unknown
update
vwait

So yes, dict was not available...

luc.de...@gmail.com

unread,
Mar 27, 2018, 9:02:55 AM3/27/18
to
> That is the thing, we have only seen what he OP has posted here, which
> may have had some extra curly brackets added on the receiving side.
>
> OP, any chance to show the raw output by the sending side?


What I've post is excatly what I receive. I've a variable and when I print it I've :

{{
name popo
nodeaddr 1.2.3.4
port 80
}} {{
name papa
nodeaddr 4.3.2.1
port 80
}} {{
name apap
nodeaddr 5.4.3.2
port 80
}}

Am I doing it wrong ?

If I use the Ralph's method, which is faaaaar better than the horror I've produced, it's working.

foreach elt $mytable {
foreach {key value} [lindex $elt 0] {
puts "$key = $value"
}
}

Thanks a lots guys, when i've posted here, I was really lost. I'm still lost but you pointed a lot stuff to dig.

rene

unread,
Mar 27, 2018, 10:10:21 AM3/27/18
to
Hello,

As far as i understand you have a variable p.e.

set var {
{{
name lala
nodeaddr 8.5.2.1
port 80
}} {{
name lolo
nodeaddr 5.2.1.4
port 80
}}
}

This variable is a list of lists.
To put it into an array you can do:

set i 0
foreach l $var {
foreach {n v} [lindex $l 0] {
set table($i,$n) $v
}
incr i
}

Now you can get the values with p.e.:

set table(0,name)

or print all with

parray table


HTH
Rene

Ralf Fassel

unread,
Mar 28, 2018, 5:49:27 AM3/28/18
to
* luc.de...@gmail.com
| > That is the thing, we have only seen what he OP has posted here, which
| > may have had some extra curly brackets added on the receiving side.
| >
| > OP, any chance to show the raw output by the sending side?
>
>
| What I've post is excatly what I receive. I've a variable and when I
| print it I've :

Can you show the code that actually *sets* the variable?

R'
0 new messages