Forum OpenACS Development: how to make your var present with upvar conflict

Hi,

I just recently made weird proc which I needed. list_of_lists _to_multirow

Since this proc emulates db_multirow things work fine except for < if @foo@ not nil > does not work but < if @foo:rowcount@ not nil > works. Since they are different vars in in reality (i think I am not an expert tcler) so naturally "if" does not see foo. But how can I make the var present without colliding with the upvar on my proc. How does db_multirow do it?

Collapse
Posted by Jun Yamog on
Hmmmm I must be tired and sleepy but I knew db_multirow sets foo to be able to test at "if not nil".  Now it does not work.  So I guess db_multirow and list_of_lists_to_multirow works alike...

Oh brother I must be too tired.

Actually, a proc already exists which converts a list of lists to a multirow data source. The name of the proc is somewhat misleading, as it is called template::util::list_to_multirow.

As far as I can tell, multirow datasources don't set anything that can be used for the not nil test. I've always used <if @foo.rowcount@ gt 0> to test for not nil, so I've never noticed the problem of not nil failing to work. It's not quite the same thing, but if you do a db_multirow query that returns no rows, it will still set the rowcount to 0.

Collapse
Posted by Jun Yamog on
Hi Dan,

It seems that list_to_multirow does not cut it for me.  Or maybe I am not using it properly.  Can you check my list_of_lists_to_multirow code?  Basically I am able to use @foo.column@ on .adp while list_to_multirow does not.

Please tell me if what I created is redundant.  Thanks.

Basically your function does the same thing.  The only difference is the formatting of the data.  You pass in two lists.  One is a list with the column names, and the other is a list of lists with the multirow data.  template::util::list_to_multirow takes a single list of lists that already has the column names embedded in the sublists.  The only real difference is where the list of lists is formatted.  Your function does the formatting of the sublists, while template::util::list_to_multirow expects it to be done externally.

Instead of creating a new proc with a similar name, who don't you patch template::util::list_to_multirow to take an optional -column_names argument and incorporate your functionality into it?

Collapse
Posted by russ m on
Something else about jun's [list_of_lists_to_multirow], it looks to me like it'll only work if there's an even number of columns. Where it's [lappend]ing to onerow, onerow already contains the column data. Shouldn't that be a fresh list being used for the [array set]able list?
Collapse
Posted by Jun Yamog on
Hi Russell,
<p>
You are correct that it only accepts even number of element lists.  list_to_multirow also suffers from this behaviour.  I have now corrected it on the latest ver.  Although it requires you to pass in the colnames for this.  No colnames is kinda like {{element val} {element val}}.  I think.
<p>
Dan,
<p>
I have revise my code to follow the same behaviour of list_to_multirow when no colnames are passed.  Here is a snip code:
<pre>
set mylist [list [list a b] [list d e]]
template::util::list_to_multirow multi1 $mylist
hw_essay::list_of_lists_to_multirow -varname multi2 -listoflists $mylist
hw_essay::list_of_lists_to_multirow -varname multi3 -listoflists $mylist -colnames [list col1 col2]
set test ""
for { set i 1 } { $i <= ${multi1:rowcount}} { incr i } {
    append test "multi1 = [array get multi1:$i] multi2 = [array get multi2:$i] multi3 =[array get multi3:$i]"
}
</pre>
<p>
The out put will be
<pre>
multi1 = rownum 1 a b

multi2 = rownum 1 a b

multi3 =rownum 1 col2 b col1 a
------------------------------------------------------

multi1 = rownum 2 d e

multi2 = rownum 2 d e

multi3 =rownum 2 col2 e col1 d
</pre>
<p>
Looks similar.  Although list_to_multirow suffers from a bug as pointed by Russell.  So "set mylist [list [list a b c] [list d e f]]" will give an error.  Using "hw_essay::list_of_lists_to_multirow -varname multi3 -listoflists $mylist -colnames [list col1 col2 col3]" things will be ok.
<p>
I will leave the decision to you about the code: if its good enough to be at template util procs.  If it will patch list_to_multirow.  If it will be an additional proc.  If it will depracate list_to_multirow.
<p>
Here is the latest
<a href="/sdm/one-patch.tcl?patch_id=352">code</a>.  Please see if there are bugs, I am bit busy right now so no big test was done on it.

You're using the template::util::list_to_multirow proc incorrectly. The sublists in the list of lists that you pass to it should be formatted so that they can be passed to array set:

set mylist [list [list col1 a col2 b] [list col1 d col2 e]]
template::util::list_to_multirow multi1 $mylist
set test ""
for { set i 1 } { $i <= ${multi1:rowcount}} { incr i } {
         append test "multi1 = [array get multi1:$i]\n"
}

Which will give you:

set test
multi1 = rownum 1 col2 b col1 a
multi1 = rownum 2 col2 e col1 d

What I suggested previously was that you could patch template::util::list_to_multirow to also work with your format where you pass in a separate column name list:


set mylist [list [list a b] [list d e]]
set col_names [list col1 col2]
template::util::list_to_multirow multi1 -column_names $col_names $mylist

and both versions would give the same result. The only difference would be where the list is formated. Your version formats the list internally in the proc, and the original version expects that the list is passed to the proc pre-formatted so that the sublists match the array set format.

Collapse
Posted by Jun Yamog on
Hi Dan,
<p>
I am sorry that I did not properly use list_to_multirow.  I think it would best that this comment is placed on the ad_proc
<pre>
Accepts a list of lists with the following format
{{row1col1 row1val1 row1col2 row1val2} {{row2col1 row2val1 row2col2 row2val2}
{rowXcol1 rowXval1 rowXcol2 rowXval2}}
</pre>
I have tried to use the current code:
<pre>
set mylist [list [list col1 a col2 b col3 c] [list col1 d col2 e col3 f]]
set mylist2 [list [list a b c] [list d e f]]
template::util::list_to_multirow multi1 $mylist
hw_essay::list_of_lists_to_multirow -varname multi2 -listoflists $mylist
hw_essay::list_of_lists_to_multirow -varname multi3 -listoflists $mylist2 -colnames [list col1 col2 col3]
set test ""
for { set i 1 } { $i <= ${multi1:rowcount}} { incr i } {
        append test "multi1 = [array get multi1:$i]
multi2 = [array get multi2:$i]
multi3 = [array get multi3:$i]
"
}
</pre>
Results in the same output:
<pre>
multi1 = rownum 1 col2 b col3 c col1 a
multi2 = rownum 1 col2 b col3 c col1 a
multi3 = rownum 1 col2 b col3 c col1 a
multi1 = rownum 2 col2 e col3 f col1 d
multi2 = rownum 2 col2 e col3 f col1 d
multi3 = rownum 2 col2 e col3 f col1 d
</pre>
But I think it would be better if we just create a another proc that accepts colnames and arranges the list according to what list_to_multirow accepts.  This proc will then just call list_to_multirow after putting in colnames to the list.  What do you think?