Variable scope question

139 views
Skip to first unread message

Werner

unread,
Aug 26, 2020, 3:16:30 PM8/26/20
to TiddlyWiki
Good evening guys, me again.

I understand that the scope of a variable is defined by the enclosing <$vars> <$set> or <$wikify> widgets. I also understand that any new <$set> widget opens up a new scope, where a variable <myVar> defined in an outer scope would be overridden. I am facing a problem where I would need to access out-of-scope variables (or come up with a completely different approach).

I am still working on a set of double-nested JSON data (using Josh Fontany's JSONmangler plugin). I want to display the content of the data in a table using table cells spanning multiple rows like <td rowspan = "5">. The problem here is, the rowspan is defined by the number of elements in the lowest nested level and I would need it before rendering the table and looping through the array elements fetching the data. So typically, in a garden variety programming language, I would do something as follows:

totalRows = 0
Loop through Level1
   nestedRows= Level2.count()
   totalRows += nestedRows
End Loop

Could anybody enlighten me, if a construct like this is possible in TW and how I would achieve it?

Two fallback options:
- storing the number of elements in the JSON structure (yuck - feels like cheating).
- throwing the whole JSON data structure at an JS macro. Positive side effect: I would have to dive into it and learn something new.

Thanks for helping me out on this.
Best, Werner





Saq Imtiaz

unread,
Aug 26, 2020, 4:41:16 PM8/26/20
to TiddlyWiki
I'm a bit tired so this isn't as clear as I would like, but hopefully this pseudocode will point you in the correct direction for the pattern for getting this done with just wikitext:

\define processRow()
nestedRows = level2 +count[]
<$vars cnt={{{[<cnt>add<nestedRows>]}}}>
<$list filter="[[allrows] +[after<currentTiddler>]]" emptyMessage="<<cnt>>">
<<processRow>>
</$list>
</$vars>
\end

\define processAllRows()
<$vars currentTiddler={{{[allrows] + first[]}}} cnt="0">
<<processRow>>
</$vars>
\end

<td rowspan=<<processAllRows>> > or set it to a variable for re-use.

TW Tones

unread,
Aug 26, 2020, 10:01:09 PM8/26/20
to TiddlyWiki
Werner,

The exact detail needs to be worked out but I agree with Saq its all doable in wikitext. I also use the HTML table tags rather than tiddlywiki table markup. As long as you do not break the rules of html tables its easy to have a variable number or rows or columns in a table if you wrap that element in another and use a list to iterate the items. Boarders and setting column title sis a little more tricky but doable

Rather than rowspan you can iterate the cells but display: none; and other methods.

Also remember you can use the count widget or count operator to determine how many items in a set and use the range operator to iterate them once you know.

Effectively you nest list widgets within table elements.

Regards
Tony

Werner

unread,
Aug 27, 2020, 12:36:01 PM8/27/20
to TiddlyWiki
Thanks, Saq, so this means you are using recursion? Didn't realize this was possible in TW. I will yet have to try it out and get back to you.

Werner

unread,
Aug 27, 2020, 1:02:40 PM8/27/20
to tiddl...@googlegroups.com
Thanks, Tony, for pointing me at the range[] operator. Could you elaborate a bit on how you use it. So far I've gotten by using the <$list> widget, but your approach might be more efficient than mine.

Here's what I'm doing when looping through JSON data

<$set name="actions" filter="[all[current]indexes[Actions]sort[]]" > <!--- indexes[] doesn't necessarily provide the correct sorting order -->
<$set name="actionCount" filter="[
<actions>split[ ]count[]]" > <!-- needed later for determining rowspan value -->
<!-- start loop through actions; fetch array indexes -->
<$list variable="currentAction" filter=<
<actions>> >
...
</$list>
</$set>
</$set>

Speaking of tables, is there anything to be aware of, as to how TW injects HTML tags? I can't seem to get <tr> and </tr> right in my dynamically created nested table. I have delegated some code to conditionally executed macros, and Chrome seems go get confused, when a </tr> is used at a position it doesn't like.

EDIT: The last issue is solved. Found a cleaner way to build the table. Set the <tr> tags at the lowest level and insert <td>s for the higher levels only when needed

Thanks again
Werner

Saq Imtiaz

unread,
Aug 27, 2020, 1:56:38 PM8/27/20
to TiddlyWiki
@werner precisely, its a recursive macro that processes each row, and calls the macro again with the next row, inside a new vars widget with the new incremented value of the variable.

The emptyMessage is triggered at the end and gives the final cumulative value.

TW Tones

unread,
Aug 27, 2020, 7:43:13 PM8/27/20
to TiddlyWiki
Werner,

An Example of using count and range for a given set of titles. Works on Tiddlywiki.com

\define all-items(items)
$items$<br>
<$list filter="[range[1,$items$]]" variable=item-number>
   <$macrocall $name=each-item item=<<item-number>>/>
</$list>
\end
\define each-item(item)
Item #$item$ {{{ [tag[TableOfContents]sort[]nth[$item$]] }}}<br>
\end

<$macrocall $name=all-items items={{{ [tag[TableOfContents]count[]] }}}/>

  • First I call all-items providing the total number of items I am going to process with count
    • This is done so I can use the $items$ as replaceable parameter in the range operator
  • Now I can process each item number, and access the specific item using the nth operator.
  • Notice the sort at the last moment (each-item) this can be changed or removed (for natural order) 
But it is important to note if you do not need the number of each item that filters naturally process the set of titles they generate, and return to the "calling process" when finished.

Nested list widgets, even recursive (call them self)  will return to the previous level when the new filter (if) it is exhausted. In effect do until end of list.

Another way is to iterate a list and and determine the position of each item in the list, each time.

For example this macro returns the position of currentTiddler in the natural list of items tagged page.
\define position() <$list filter="[tag[page]allbefore<currentTiddler>count[]add[1]]" variable=position><$text text=<<position>>/></$list>
In this case the position is determined for each tiddler, rather than using the range widget to provide the position.

Regards
Tony

TW Tones

unread,
Aug 27, 2020, 7:55:39 PM8/27/20
to TiddlyWiki
Saq,

You have thrown the cat amongst my pidgins here. I am keen to see If I can use this design to generate an end of process. There were features in the old TWC for each tiddler macro we are yet to recover.

If you make a tested demo example of this code pattern please share.

However I realise my own recent post to Werner allows a test for 1st and total count (last) to trigger  beginning and end processes.

Regards
Tones

Werner

unread,
Aug 28, 2020, 11:33:36 AM8/28/20
to TiddlyWiki
OK, here's what worked for me. It took me a bit to figure it out, especially for making it work on a JSON tree, but it works.
\define makeRowspan()
 
<$vars currentAction={{{ [all[current]indexes[Actions]sort[]first[]] }}} cnt="0" >
 
<<addRows>>
 
</$vars>
\end

define addRows()
 
<$set name="Items" filter="[all[current]indexes[Actions]addprefix[Actions/]addsuffix[/Items]]" select=<<currentAction>> >
 
<$set name="itemCount" value={{{ [all[current]indexes<Items>count[]] }}} >
   
<$vars cnt={{{ [<cnt>add<itemCount>] }}} >
   
<$list variable="currentAction" filter="[all[current]indexes[Actions]sort[]after<currentAction>]" emptyMessage="<<cnt>>" >
     
<<addRows>>
   
</$list>
   </
$vars>
 
</$set>
 </
$set>
\end

and then in the <table>

<$wikify name=rows text=<<makeRowspan>> >
<td rowspan=<
<rows>> >
...

Thanks so much, Saq and Tony, this took me a lot further on the learning curve. Without you, I would have never come across using first[] and after[].

Best, Werner
Reply all
Reply to author
Forward
0 new messages