Equivalent of an array of objects

Can I pass the equivalent of an array of objects to a macro?

In another thread, I’ve been talking about a plugin I’m building. It generates some additional markup in a ViewTemplate, and I was looking at making that markup configurable.

I would supply the titles of the previous and next tiddlers, and the list off all tiddlers in a particular list, and the a user could override the default macro to change from my table-based markup to one that suits her better. I could probably work that out with a filter. But the catch is that in some cases, there will be multiple such entries, and they might want to mark them up jointly, as I do now with my <table>-based markup.

I could work this out by supplying three overridable macros, one for before the entries (e.g. <table>), one for after (</table>), and one for each entry (<tr>...</tr>), but that no longer feels like clean customization.

I would much rather have a single macro. In a programming language like JS, I would simply pass something like

[{
  title: 'Pathway', 
  prev: ' Maecenas sed enim', 
  next: 'Ut tellus elementum sagittis', 
  all: ['Tempus quam pellentesque', 'Cras fermentum', ... ]
}, {
  title: 'Tutorial', 
  prev: 'Nunc sed blandit', 
  next: 'Semper auctor ', 
  all: ['Lorem Ipsum', 'Feugiat scelerisque', ... ]
}]

or perhaps an object keyed off title with values that were themselves objects with the other properties.

But I know of no way of doing something like this. Is there any way to supply a macro with a list of lists of tiddlers or other structures that might handle this?

There is no equivalent to an array of objects in TW5. But you can have a title list (a list of titles) which in turn refer to tiddlers that can contain fields. So there are list fields (which, except for “list” and “tags” are just regular fields that you remember to treat differently) and title lists (the input or output of a filter, for instance). A title list can be represented as a string (e.g. “[[The Cat]] [[El Gato]]”) and then turned back into a title list via the enlist or enlist-input filter operators. Title lists can be turned into strings through the used of the reduce filter run expression possibly in association with format:titlelist[] .

Now I hope someone will pop up and tell me that there’s been an array object ever since 5.2.x …

2 Likes

Making configurable items

  • Store filters, tiddler names etc… in $:/config/tiddlername tiddlers then transclude it to get the value into your code
  • Just redefine a variable or macro to override an existing one
  • TW 5.3.0 will even introduce the ability to override widgets and a lot more.

I will link to a related discussion here soon [edited] In the spirit of Open Dialogue - Avoiding the YAP, YAP - Yet another Plugin?

Objects
It actually makes sense to consider tiddlers as objects and that is already what they are in many ways, but it all of course depends on what you mean by object.

  • When writing solutions on tiddlywiki I incidentally use the fields system-object (eg viewTemplate) and object-type (eg contact, project) to define what tiddlers are.
  • If you are thinking about objects as “Object oriented programming” I believe most of the features “abstraction, inheritance, encapsulation, and polymorphism” or “methods and properties” can be emulated in one or more ways.
    • This only issue may be when something is triggered when you want to modify the tiddler store. But there are good tricks to help.
    • An outline of OO

Arrays?

  • JSON scan store arrays
  • List, title lists, variables, list fields and sets are all quite interchangeable with arrays
  • The set widgets “select” is an “optional zero-based index of the item to return from the filter output”
  • Data tiddlers using the index parameter on a number of widgets also can act as an array.
  • We are all born naïve about numerous subjects. We don’t inherit TiddlyWiki skills.
  • Fortunately if you can ask a well phrased question we can help you here in the forum.
  • You can either pass the whole list to the macro or the name of a macro, variable or reference to the list.
  • Use the macrocall widget to call macros with more advanced parameters such as param1=<<var1>> param2={{!!fieldname}}
  • Call a macro with a variable name and then use the getvariable Operator

Perhaps you could request something a little more specific.

This is a plugin that uses a ViewTemplate to add a footer to every tiddler that meets certain criteria. So I would like to pass to the macro (or custom widget if that’s indicated; I haven’t tried those yet, though) three items derived from every Tag that’s indicated by that criteria (more details about the criteria at the plugin’s site): the name of the tag, the previous item to the current tiddler in that Tag’s list, and the next item. I was thinking that I should supply all items, and might still, but that’s easily derived from the tag name.

But I don’t think there’s any way to store these in a specific tiddler: this is information found only at tiddler-render time.

Yes, that’s exactly what I’m looking to do. Right now my plugin has a ViewTemplate tiddler, and the plucky user can override that to change the markup. But that mixes together the logic for selecting the correct tiddlers to show with the markup to format them. I was thinking it might be a useful refactoring to extract the markup into a macro, pass it the correct data, and have it do the formatting. Then the user has a very clean way to change the markup: just override that macro. I’m just looking for a way to pass that data.

Good to know.

In this case, I have no JS code in the Plugin, only Tiddlywiki. I used the Plugin as the cleanest way of deploying this code together. And in fact, this question is very much in the spirit of that discussion. I’ve looked at a few plugins that use JS, and I’m confident I could do something like that for certain purposes, but I’m trying to learn what is the most idiomatic way of doing this in TW, or if there is even the facility to do so.

Yes, and I’m thinking of them the same way, I believe, as a collection of key-value pairs.

Some of that might well clean up my current implementation. I hadn’t seen select, and will check it out.

Well, that’s a clear failure, I guess. I was trying hard to give as much information as I could. Here is a very slightly simplified version of the current ViewTemplate:

tags: $:/tags/ViewTemplate
title: $:/plugins/ScottSauyet/WizardNav/ViewTemplate
type: text/vnd.tiddlywiki

<$set name="currentNavStep" value={{!!title}}>

<table class="wizard-nav">
<$list filter="[is[current]tags[]tag[Wizard]sort[]]">
  <tr>
    <td>
      <$list filter="[enlist{!!list}] :else[tag{!!title}] +[before<currentNavStep>]">
        <$link to=<<currentTiddler>>>« {{!!title}}</$link>
      </$list>
    </td>
    <td>{{||$:/core/ui/TagTemplate}}</td>
    <td>
      <$list filter="[enlist{!!list}] :else[tag{!!title}] +[after<currentNavStep>]">
        <$link to=<<currentTiddler>>>{{!!title}}  »</$link>
      </$list>
    </td>
  </tr>
</$list>
</table>
</$set>

(I just noticed a bug in this, which I’ll try to correct soon: it yields empty tables for non-matching tiddlers. It shouldn’t have any markup at all then.)

If I was guaranteed that [is[current]tags[]tag[Wizard]sort[]] returned only one tag, then I could extract a macro leaving this view template something like this, which is almost certain to have incorrect syntax – I’m still learning – but should demonstrate the idea:

<$set name="currentNavStep" value={{!!title}}>
<$list variable="tag" filter="[is[current]tags[]tag{$:/plugins/ScottSauyet/WizardNav/config/TagName}first[]]">
<$set name="prev" value="[enlist{tag!!list}] :else[tag{!!title}] +[before<currentNavStep>]">
<$set name="next" value="[enlist{tag!!list}] :else[tag{!!title}] +[after<currentNavStep>]">

<<WizardNavFooter $prev$ $next$ $tag$>>

</$set>
</$set>
</$list>
</$set>

alongside an overridable macro something like this (same syntax warning, but probably more so!):

\define WizardNavFooter(prev, curr, tag)
<table class="wizard-nav">
  <tr>
    <td>
      <$link to=<<prev>>« {{prev!!title}}</$link>
    </td>
    <td>{{tag||$:/core/ui/TagTemplate}}</td>
    <td>
      <$link to=<<next>>>{{next!!title}}  »</$link>
    </td>
  </tr>
</table>
\end

The trouble is there can be more than one, and even if I clean up the syntax, I don’t know how I might pass something vaguely equivalent to [{prev1, next1, tag1}, {prev2, next2, tag2}, ...] to the macro so that it could loop over the various records to create rows. And that’s what I’m looking for advise on. Is there any reasonable way to do that? Can people suggest good alternatives?

Thank you. I don’t see any way to stretch that to cover this case. But I could well be missing the implication. Now that there’s more details here, can you see it working?

Thank you both! Even if I can never manage to do what I was hoping, I’m learning a lot.

Have you concidered the following?

  • [is[current]tags[]tag[Wizard]sort[]first[]]
  • or `[is[current]tags[]tag[Wizard]sort[]llimit[1]]

I have being working on a Next Previous that appears in the tag dropdown, I dsont have the time to finalise it, but in case it assists here is an excerpt that shows some useful filters

<$set name=prev value={{{ [tag[$tagname$]sort[$sort-field$]before<currentTiddler>] }}}>
<$set name=first value={{{ [tag[$tagname$]sort[$sort-field$]first[]] }}}>
<$set name=next value={{{ [tag[$tagname$]sort[$sort-field$]after<currentTiddler>] }}}>
<$set name=last value={{{ [tag[$tagname$]sort[$sort-field$]last[]] }}}>

Yes, and I use that as a way to choose whether to show the block or not. But the point is that I need all such entries. I show a footer row for every tag that is itself tagged Wizard. That’s the reason for this thread. I can collect prev/next/title values for each such footer, but I don’t know a useful way to pass the group of them to be used in a simple overridable macro.

There’s a tendency when one comes from a programming background to lean on macros, because they look so much like the functions with which you are familiar. But they’re not functions – all they do is text substitution.

It’s generally better to concentrate on transclusions, especially in the context of templates. I haven’t followed your code too intensely, but generally what you would do is have some sort of outer list widget that steps through all the tiddlers you’re interested in. Then have a template that appears on each target tiddler that calculates the next/prev value. So usually there would be no need to create a list of next/previous values. I guess I need a higher level understanding of what you’re trying to accomplish.

Good luck!

I am still struggling a little to understand where you are having trouble vs the outcome you are seeking (my bad not yours), however it seem likely you already have the information you need to make this work, we just need to get the right focus.

The following is a few items of information that may help you resolve this;

Here is my "translation of this filter [is[current]tags[]tag[Wizard]sort[]]

  • is[current] the current tiddler,
    • what you could not know is [all[current] is more efficient giving the same result, arguable [<currentTiddler> even more so
  • tags[] says list the titles of all tags on the current tiddler
  • tag[Wizard] says for each title from above (the tags) test if that tag has the tag wizard.
  • Then you sort the titles (tags also tagged Wizard)

I may be wrong but perhaps you mean [is[current]tag[Wizard]tags[]sort[]] if the current tiddler is tagged Wizard give me a list of all its tags (including Wizard).

Of note is using a tags list field is not always reliable, it is only populated if you try and reorder the items in the tag list.

  • it is more reliable to use tag[tagname] but most likely you need a variable such as tag<tagname>.

I see you using [enlist{tag!!list}] which means

  • Create a title list from the content of the list field, in the tiddler named “tag”
    • This does not sound right to me!

I think you already understand that a lot of similar problems can be solved with nested lists. ie one list inside another. the first (outer) identified each title we want to use the second, inner list uses that title and does what we want to each found in the outer list.

In this code {{tag||$:/core/ui/TagTemplate}} are you intending to get a tag pill for the tag parameter?

  • Perhaps you mean {{$tag$||$:/core/ui/TagTemplate}}
  • But if “tag” is a variable you can to do the following
    • <$macrocall $name=tag tag=<<tag>>/>
    • or
<$tiddler tiddler=<<tag>> >
<$transclude tiddler="$:/core/ui/TagTemplate"/>
</$tiddler>

Best of luck

Nobody’s bad. Miscommunication happens all the time. Thank you for your efforts and for persevering.

I’m afraid you may be missing the point that this is working pretty well already. The plugin does what it’s supposed to do, and I think is getting pretty close to clean. I just want to make it somewhat more configurable by letting experienced users override the markup it generates.

This seems relevant:

Thank you. That makes sense. I will try to take it into account in the future and in the next clean-up of this code.

No, that does just what I want it to do. The idea is that there are some linear paths through a subset of the tiddlers. My plugin is adding previous/next footers to these tiddlers. To designate a path, I use a TagTiddler, a tiddler with a name to be used to tag all the tiddlers in the path. That TagTiddler must itself be tagged “Wizard”.

Minor aside

In the actual code the name "Wizard" is abstracted into the text field of a tiddler named $:/plugins/ScottSauyet/WizardNav/config/TagName, to allow users to choose a different name than Wizard, useful for instance if their wiki is full of swords-and-sorcerers content. But calling it "Wizard" here simplifies things.

So that filter does exactly what its supposed to do: collects all the pathways that contain the current tiddler. I would expect that in most cases, a tiddler will only be a part of a single pathway, but I’ve already found exceptions in the project that spawned this plugin, so I must be able to handle it.

And I do handle it well enough. It works for me, and at some point I will certainly say that it’s good enough, whether or not I solve this issue. But if I can, I would like to do that extra refactoring in a way that makes overriding the markup clear.

It’s doing what I would like it to. This was suggested by @EricShulman in response to an earlier question. The trick is that if the TagTiddler has a list field, then we should use it, but if it doesn’t, we still need to get all the items tagged with it.

If there’s a better way to do this, I would love to know.

No! That would solve my problems. Although I asked for a list of object-like entities, a list of lists would solve my problem just as well. Do you have a reference for this, or a short explanation? How would I create one? I think I can figure out the macro syntax if it gets a list of lists. This would be wonderful!

Yes. I will probably eventually change that a bit, because I want to highlight the current step and ensure that the current step is in the current window if this scrolls too far. But that’s a much more minor concern to me now. If I can solve the other problems, I’ll get to this.

That’s probably true of me. But I think in the end, I’m planning on using them appropriately in this case. This overridable macro takes some input fields and creates some HTML/wikitext that will become footers for a collection of macros.

Probably the easiest way to see this is to look at the difference between one footer and multiple footers. If I were to refactor so that there is an overridable macro that handles the markup of the whole footer section, then how would I pass the data that macro needs to show one, two, or many footer blocks? My markup is a single table, with a row per title (“Tutorial” or “Pathway” here). But that means I can’t cleanly use a per-row macro, without also making them implement before and after markup? Creating <table> and </table>, for instance. But if I can pass the whole set at once, then I think I can make this clean enough. @TW_Tones says that there is a way to create a list of lists. That would do well enough for this case, and I’m going to go searching for that information after I have some dinner.

As always, thank you both for your help!

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