Equivalent of an array of objects

I think I understand the general Gist of your problem now.

  • Is the problem when you are trying to generalise something such as $:/plugins/ScottSauyet/WizardNav/ViewTemplate “the markup”, in this case the table, the logic, it is embedded in the markup and its hard to switch to an alternative without it to needing the same logic embedded in it?
    • This makes it less adaptable because its hard to switch markup or the template if the template must have the logic in it.

If the above is close to you problem, let me know. I have come across this multiple times and can suggest a few alternative approaches.

  • Hint: You must seperate the logic from the “markup” or presentation of the result.

Of note @Mohammad has addressed this kind of issue multiple times for wizards, presentations and slideshows. He is possibly our expert on this.

  • Could you request a simpler example of this seperate from the larger problem, I am sure I could address that, and simply?
  • However if my above description of your problem above is correct I think it can be answered.

Yes, and I already have a plan for that. I know how to do all the pieces, except for how to pass the data from the ViewTemplate to the macro I excise from it. Based on your previous comments, I will try to figure out how to do both the generation of lists of lists (still have to figure that our) and (easy I think) the process of using that in my default macro. So any references to lists of lists would be welcome.

This may be a bit simpler, although it’s pretty close to my problem:

Given a list of titles, how do I create a list containing a list for each one, that inner list consisting of the first and last element that are tagged with that title?

I’ve been busy with the actual project that is already using this, so I haven’t spent any more time on this except to respond to threads I’ve started. But I will look soon at what I can do with the Map filter run prefix which looks likely to be of help.

I don’t understand why you need to send a list of anything. If your macro has:

\define WizardNavFooter(prev, curr, tag)

Isn’t it enough to just send prev, curr, tag ?

<$list filter="[tag[Wizard]]">
<$list filter="[<currentTiddler>tagging[]first[]] [<currentTiddler>tagging[]last[]]">

</$list>
</$list>

I haven’t followed the whole thread closely, but is this the sort of behavior you’re looking for? I used [tag[Wizard]] as a placeholder for whatever outer title list you need; you can, of course, replace it.

Perhaps the following starts as an Over simple example of a list of lists, on tiddlywiki this macro uses the list of tiddlers tagged TableOfContent (as a list of lists) then iterates each of those as a list.

  • It is worth working through because I set up an answer to your bigger problem.
<$list filter="[tag[TableOfContents]]" variable=list-of-lists>
   <$list filter="[tag<list-of-lists>]" variable=list-item>
      <$macrocall $name=tag tag=<<list-of-lists>>/> - <$macrocall $name=tag tag=<<list-item>>/> <br>
   </$list>
</$list>
  • I suspect this is trivial
  • Just change the filters to access the required list-of-list and items within each
  • The centre line is where you could replace it with a test against the current tiddler, to find the next/previous.

But we may want to test where the current tiddler is situated in each of the tags it has;

This one returns the position in the list of all tags

<$list filter="[all[current]tags[]]" variable=list-of-lists>
   <$list filter="[tag<list-of-lists>]" variable=list-item counter=item>
      <$list filter="[<list-item>match<currentTiddler>]"> <$link/> <<item>> in <<list-of-lists>><br>
   </$list>
</$list>
  • But again in the middle you could determine which is next previous
  • If you tag the tiddler containing the above with $:/tags/ViewTemplate every tiddler will show where it is in the list on that tag.

Now consider what is written between the lines;

outside heading
<$list filter="[<target-tiddler>tags[]]" variable=list-of-lists>
  Each list heading
   <$list filter="[tag<list-of-lists>]" variable=list-item counter=item>
      List item
   </$list>
  Each List footer
</$list>
outside footer
  • unlike when you use “MAP” nested lists have a place for you to “handle” the beginning, middle and end of each nesting. Lets call these places I marked with simple text as elements.
  • In your current solution you have a pile of html table tags for the different elements table heading rows footer etc…

Note:

  • I think people can make filters unnecessarily complex trying to “shoe horn” nesting into them eg using MAP, when they can just use nested lists, and at the same time they gain access to intermediate variables and positions to place beginning, middle and end code.
    • I am confident there is rarely any performance differences between them as well.

So for your multiple mark up solution, you can conditionally choose the element need for each alternative mark up place.

  • I will give some additional examples if asked.
  • But conditional transclusion may be the way
    <$transclude tiddler={{{ [[$:/template/elementname/]addsuffix<selected-markup>] }}}/>
  • Where elementname is list-heading item-heading list-footer etc
  • and the selected selected-markup variable may contain table, list etc…
    • You would then have a tiddler for each combination elementname/selected-markup

Finally,

There are other ways to do the above such as using $set filter to store a list then using $set with the index parameter to access each item on a 0 based index etc… or using data tiddlers, but I think this is the most straight forward.

  • In the past I have also built dynamic tables that have an additional list to identify and iterate the columns - this ends up also being needed in the header a footers

I just publish an Easy Next, Previous, First and Last TAGGED on the tag pill that may be of interest.

It would be easy to co-opt for other solutions.

If I have a situation where Ints, Odds, and Primes all have the tag Wizard, and Tid7 has tags Ints, Odds, and Primes, then I want to supply the information that WizardNavFooter can use to generate something like this:

<table>
  <tr> <th>prev</th> <th>title</th>  <th>next</th> </tr>
  <tr> <td>Tid6</td> <td>Ints</td>   <td>Tid8</td> </tr>
  <tr> <td>Tid5</td> <td>Odds</td>   <td>Tid9</td> </tr>
  <tr> <td>Tid5</td> <td>Primes</td> <td>Tid11</td> </tr>
</table>

For that I need some format that groups Tid6, Ints, and Tid8 in one collection, Tid5, Odds, and Tid9 in a second one, and Tid5, Primes and Tid11 in a third. And of course there’s no fixed limit to the number of rows. Perhaps we might add a new Wizard-tagged TagTiddler Periods, which tags Tid24, Tid7 and Tid365, or some such. The macro should just take that in stride.

So a single macro that only handles one row could be done, but then there would need to be others to do <table><tr><th>...</th>... </tr> and another to do </table>, since the whole idea is to allow customization of the entire footer, and requiring multiple macros for that seems to spoil the cleanliness of such a refactoring.

It looks as though @TW_Tones has some useful suggestions, but I won’t have a chance to test them until this evening at the earliest.

I tested it, and it does do something somewhat equivalent to my current solution. I’ll look at it more closely this evening or over the weekend to see what I might learn from it and adapt to my own. But I don’t think it does anything like what I’m looking for in this thread. My recent response to @Mark_S has perhaps a simpler example of the issue I’m trying to solve.

Thank you, that’s a perhaps cleaner version of what I’m doing now. What I would like to do is a little different. Perhaps my recent response to @Mark_S would make it clearer.

Thank you for all the effort you’re putting in here. I won’t have time to even try these suggestions until this evening or tomorrow. But I will spend time with them, and learn from them. And again, thank you very much!

Instead of an overridable macro, could you make an overridable tiddler? And available to that tiddler is a variable called footers or something?

I’m thinking a bit like Projectify does, here: Projectify/AddTodo.tid at c14cdda89255061a7f1ac6fd6402692e067287ae · ThaddeusJiang/Projectify · GitHub

Except instead of currentTiddler it would be something named the way you want.

That way a user could override your plugin’s shadow tiddler with a customized template. Would that solve this?

That variable could also be a filter the tiddler can use to get the list itself and decide what to do based on its count.

1 Like

This is very rough (and doesn’t make use of a macro), but I just tested it in a new template tagged with $:/tags/ViewTemplate, and I believe it ought to generate the kind of table you’re describing.

<$list filter="[<currentTiddler>tags[]tag[Wizard]limit[1]]" variable="null">
<table>
<tr>
<td>Previous</td>
<td>Category</td>
<td>Next</td>
</tr>
<$list filter="[tag[Wizard]] :filter[<..currentTiddler>tag<currentTiddler>]" variable=category>
<tr>
<td>{{{ [tag<category>before<currentTiddler>] }}}</td>
<td><$link to=<<category>> /></td>
<td>{{{ [tag<category>after<currentTiddler>] }}}</td>
</tr>
</$list>
</table>
</$list>

A few notes:

  • The outer list is there to ensure that the table is only rendered on tiddlers with one or more tags that are themselves tagged Wizard.
  • I used a ViewTemplate because it’s the simplest way to automatically render the navigation table on all appropriately tagged tiddlers. If I were building this for myself, I’d probably use the View Template Body cascade mechanism to handle conditional display rather than nesting it all inside a list, but this is simpler to share.
  • If you replace [tag[Wizard]] with something like [tag{!!navigation-tag}] then users can set the specific uber-tag they want to use by adding it to a field called navigation-tag on the template tiddler. I’d probably go ahead and add the field with “Wizard” as its default contents.
  • I’m not sure if you’re interested in including links to jump to the first/last tiddler in a sequence, but if so, it should be pretty simple to add those columns.
1 Like

I guess the general answer would be to break it into multiple, easy-to-read macros. Then, if you want to change things, you change two macros. In the following, you can change the structure by changing the overall macro mytable and the individual rows in myrow. Each is very understandable on its own. And macro myrows – the one with the basic logic – doesn’t have to change at all. Sometimes it’s best to roll with the paradigms you’re given, rather than the twenty cents you thought you wanted.

\define mytable()
<table border=5>
  <tr> <th>prev</th> <th>title</th>  <th>next</th> </tr>
   <<myrows>>
</table>
\end
\define myrows()
<$list variable="tag" filter="[all[current]tags[]tag{$:/plugins/ScottSauyet/WizardNav/config/TagName}]">
<$vars 
  prev={{{[<tag>get[list]enlist-input[]allbefore<currentTiddler>last[]]:else[<currentTiddler>]}}}
  next={{{[<tag>get[list]enlist-input[]allafter<currentTiddler>first[]]:else[<currentTiddler>]}}}
>
<<myrow>>
</$vars>
</$list>
\end

\define myrow()  <tr> <td><<prev>></td> <td><<tag>></td>   <td><<next>></td> </tr>

<<mytable>>
1 Like

Well, I was assuming that the macro would be stored in a tiddler. If there is a way to create additional variables to be used in processing a tiddler, that would make it nicer still (no define ... end). But I don’t know how to do so. I assumed variables like currentTiddler were buried in the bowels of TW code. I see from your code that a simple transclusion seems to keep the variables alive. I will probably use that to clean up my code now that you point that out.

But I’m not sure that it offers anything for the larger problem of how to pass a list of lists or something similar (or now as you’ve pointed out, not pass them so much as store them in a variable the view could use). But I have a lot of reading to do, thanks to @TW_Tones.

Thank you very much for your help!

That might be best. But I’m learning a lot more about TW pursuing this than I would by sticking to the tried and true. I keep thinking that there’s an answer obvious to the more experienced, that I’m either too dense or too inexperienced to see. But it simply may not be.

I wouldn’t bother refactoring what I’ve got, though to a three-macro (or I guess just three-tiddler, per @drhayes) solution. I don’t think that gives enough benefit over the current just-override-the-view-template solution. But I haven’t tried.

And I have a separate objection, which is that the minute I do that, I’ll see some use for rules like: “If there are more than three rows, do X, otherwise do Y” or “Let me handle the odd rows differently than the even ones.” Sure, it’s easy enough to say that such is not supported, but the only reason to even try this refactoring would be to offer users a simple way to customize that output while I handle the logic.

I’ve been spending a lot of time in these discussions, and I seem to have brought in a lot of people with far greater TW knowledge than my own, so, while I haven’t tried everything yet suggested, I am starting to believe that what I want is either not possible or only possible with some arcane knowledge. But I have plenty still to read and try.

Thanks again for you input.

Thanks. My current solution is similar, although your version is written more cleanly. I’d love for the user’s override not to have to include this line:

<$list filter="[tag[Wizard]] :filter[<..currentTiddler>tag<currentTiddler>]" variable=category>

or [tag<category>before<currentTiddler>] and [tag<category>after<currentTiddler>], but rather for these to be supplied to them as usable variable or some such mechanism.

I have something similar in my yet-to-be-published version.

Thanks a lot, more to study! :stuck_out_tongue_winking_eye:

That’s already there in the published version.

For my own uses, I’m not interested, but if I ever manage to get this customization the way I want to, then it would make sense to include them too. I hadn’t even thought about it, though. But you’re right, that would make sense, and be easy to do atop everything else.

Thank you for the help! (And – maybe! – for the additional material to research.)

You could certainly define the filters as variables, but I believe it comes at some slight cost to performance. Saq has written quite extensively about good practice from a performance standpoint here, but the general recommendation is to avoid macro substitution when possible.

I do appreciate your effort to make your final product as user-friendly as possible! I’d like to gently suggest that it may be counterproductive to hide too much, though. If someone is interested in tweaking the display template, it’s likely that they’ve already gained some degree of familiarity with TW code, or if not, they’re at least considering dipping their toes in - and in this case, it can be more challenging to follow a structure across multiple macros and variable definitions than it would be to study it in situ.

As a personal example, while I have tremendous respect and appreciation for Mohammad’s wealth of out-of-the-box or otherwise beginner-friendly plugins, I found them very difficult to tweak as a low-intermediate user - mostly because he’s so very good at hiding his filters!

If you want to pretend that there are arrays, you could pack everything into strings. Here titlelists are packed into one string separated by &&& (you can use whatever unique token you need). Then they can be passed, reparsed, and used again. For testing, I used tags “HelloThere” and “BetaReleaseNotes” (limited to 3 tiddlers for the image below) :

image

\define firstpass()
<$list filter="[all[current]tags[]]" variable="tag"><$wikify name=passme text="""<$text text={{{ [tag<tag>]:reduce[format:titlelist[]addprefix[ ]addprefix<accumulator>] }}}/>""" ><$text text=<<passme>>/>&&&</$wikify></$list>
\end

<$wikify name=arrays text=<<firstpass>> >
<$list filter="[<arrays>split[&&&]!is[blank]]" variable="array" counter="count" >
Array <<count>> :
<$macrocall $name="list-links" filter="[enlist<array>]" />
</$list>
</$wikify>

Thank you, one more time. I did consider something like this, but believed that it was likely to be more complex than your demonstration.

With more and more comments, I’m getting the sense that what I really want is simply not possible. I’m not overly disappointed, as this was always at most a nice-to-have. I think the plugin is already convenient and easy to use.

Hmm, thought I replied to this earlier. Got distracted by that performance article, I guess.

If I don’t use so some technique to pass data from one place to another, there is no reason for this refactoring whatsoever. But I’m quickly coming to the conclusion that this is the best answer.

I don’t think of it as hiding information as much as of bundling it appropriately. As a developer/software architect, I write a fair bit of code intended for others’ code to interact with. As much as possible I try to simplify the users’ experience, writing libraries rather than frameworks, passing the right information to the user that they don’t have to keep calling back for more (outside volume concerns like pagination.) I was hoping to apply the same here.

But thank you for your gentle advice. It sounds like it probably will be best to drop this, at least until I have much better TW understanding.

1 Like