Wikitext version of switch/nested if-else/nested ternary statements

Sorry, I have become quite occupied on other essentials, so I will give a quick dump of what I was doing in this space, yet another branch to the original question. This Topic has generated a lot of interesting approaches.

Using the following widget we call it with an arbitary set of parameters and values, in this case the value and icon pairs.

This is a designer tool and need not exist in the published wiki

\widget $jsonify.params()
\function parameters.value() [<params>jsonget<param>]
<$parameters $params=json-params>
# JSON: <<json-params>>
</$parameters>
\end $jsonify.params

<$jsonify.params yes=✅ no=❌ pending=◎/>

which displays the following;

JSON: {“yes”:“:white_check_mark:”,“no”:“:x:”,“pending”:“◎”}

Now copy the result and store in a variable;

\define json.params()  {"yes":"✅","no":"❌","pending":"◎"}
\function show.icon(value) [<json.params>jsonindexes[]match<value>] :map[<json.params>jsonget<currentTiddler>] 

# `<<show.icon yes>>` '<<show.icon yes>>'
# `{{{ [show.icon[yes]] }}}` {{{ [show.icon[yes]] }}}

Result

Snag_f5ff4db

  • Note the 2nd is italic because its a link and the :white_check_mark: tiddler is missing.

I have not done it yet but it could be simplified further but this is the basic pattern;

  • At design time generate a JSON array, make it a static variable
    • This can then be used multiple times and for arbitarly large sets.
    • You need not have variables by the name of the values.
  • Make use of this JSON array via a function
  • Use the variable reference to the function so is it not a link (unless wanted)

You can also use this method both as a condition and to get an icon

<$list filter="[show.icon[no]]" variable=icon>
Display only when filter is true AND get the icon
<$link to=<<icon>>/> 
</$list>
  • A set of the above lists would form a case statement
    • This is an importiant fact, perhaps I will explain this later.
  • Of course you could use [show.icon<value>] [show.icon{!!fieldname}] etc…
1 Like

My first post had working code, and it’s what’s in my wiki now. I just felt it was full of too much duplication, which would simply get worse if I added more cases. I was hoping for a more streamlined approach. But it is mostly just an intellectual exercise. The solution I started with is not too problematic. I simply would like something better.

In more detail, the wiki is tracking information for a local political party. There was a recent election in my town, and I wanted to capture the results. Among the other data tiddlers are ones representing candidacies, with links to the person involved and the office for which this person is running. After the results were announced, I added to these tiddlers the votes received and the yes/no field for whether the candidate won.

I make reference to these data tiddlers in several different places: In the biographical tiddler for each candidate, in the descriptions of each of the offices being sought, and in an overview capturing the entire slate of candidates. In each of these places, I want to use the symbols to denote whether the candidate was successful. So I want this to be a reusable something: macro, function, procedure, widget.

I don’t need to map these. In any single usage, I want to pass a reference to a specific candidacy – using a variable such as cand or the generic currentTiddler – to this something and get back a the right icon.

Because this is captured in a single place, the extra duplication is not a major issue, but I thought someone would be able to point me to a better pattern for future case statements.

Yes, that was something I mentioned in the initial post, and tried to implement in a recent message. (Any suggestions for what’s wrong with that attempt would be much appreciated.) And @TW_Tones has a slightly different twist on the same idea as well.

Thank you; these are great! The first option won’t do for me, but the other two make sense. The third one is the same approach @etardiff demonstrated above; it definitely helps reduce the duplication. The second one looks like a perhaps better way to do the same thing I tried and failed to do with a data tiddler.

I will try to expand a little on your second example, and report back.

Thank you very much for your help!

This is a nice alternative to having a data tiddler. I’m not sure which I prefer, but it’s very useful to have both variations available.

This doesn’t quite end up with the API I need (described more clearly in my recent response to @stobot) but I think I can wrap it up as I need.


Two questions. First, am I missing a subtlety, or can this

\function show.icon(value) [<json.params>jsonindexes[]match<value>] :map[<json.params>jsonget<currentTiddler>] 

be simplified to this:

\function show.icon(value) [<json.params>jsonget<value>]

? It works for my simple tests, but am I missing some edge cases?

Second, in this. which definitely would be my preferred way to call:

 <<show.icon yes>>

I’m going to be limited to passing a value as the parameter, correct? I can’t pass a variable reference such as <currentTiddler> or some such? I would have to switch to macrocall/transclude to do so, right?

The point is that I want to pass the name of my tiddler to the function, and have the function lookup my won field to choose the symbol.


Agreed. Many thanks to you and to all the contributors!

I can give a full answer perhaps tomorrow but we can rewrite things to be less general and brief but also consider [show.icon{!!fieldname}] against the current tiddler. But if course we could rewrite things so <<show.icon fieldname>> works

  • if realised after my reply that show icon couldnow be something like candidate.status
  • you can always write an additional macro that invokes another with set parameters rather give the parameters every time.

Note @buggyj short form

Yes, that’s what I tried to do above, but haven’t been able to get to work. Any ideas what the issue is?

Note we have the value eg yes, we just need to get its corosponding Json value :white_check_mark: if its available.

  • this has led me to realise this makes a number of code patterns easier to build in part because these little json arrays can be used in many ways.
  • obtain icons “a translation”
  • test is a valid key
  • list valid keys
  • drive the logic
  • reduce the need to filter to obtain the values and icons

I am even looking at replacing either or both the key or value with a filter.

  • and even encode a series of actions and values.

In my case, ideally, I don’t start with the value, but with a tiddler that happens to have the value in a known field. Of course this can be done with the layering approach we’ve discussed elsewhere of having one function call another, passing some parameters supplied to it and other hard-coded ones.

I don’t think we have operators with an arbitrary number of parameters, but now with a procedure or a custom widget (and transclusions) and the $parameters widget, you can write a custom widget with an arbitrary number of parameters. If you do not name the parameters they are numbered.

\procedure testparameters()
<$parameters $params=all-params>
<<all-params>>
</$parameters>
\end

<<testparameters a b c d e f >>

Results in

 {"0":"a","1":"b","2":"c","3":"d","4":"e","5":"f"}
  • You could then use a range operator to step through all parameters until is blank.

@Scott_Sauyet there has being a lot of water “under this bridge”, and many ideas suggested, but not all permutations thoroughly explored. I know I have gained a lot of inspiration and have myself run off on a number of tangents, both here and off line. It’s being very productive.

However I thought there was a few solutions to the Original Topic, implied in earlier code so I have now “lost the thread”. As a result I don’t think I can help (My bad) any further unless you “restate the question in full in a new reply” in light of what you have “learned”. It is getting hard to determine the meaning in relation to all the replies.

1 Like

I think I did that in #26, but let me try a different way.

Restatement

All of this was just looking to make code cleaner. I started with a working function that I was able to call like

<$transclude $variable="results" candidacy=<<currentTiddler>> />

In other places, I had a local variable instead:

<$transclude $variable="results" candidacy=<<candidacy>> />

(While I would prefer if I could just use it like this:

<<results <<currentTiddler>> >>

I’m fairly certain that the macro syntax won’t allow anything like that, and the <$transclude ...> syntax is not horrible.)

In retrospect, results is a terrible name. election.results, results.icon, are better, and I’m sure we could think of many more.

The code I used for this looked like:

\define results(candidacy)
<$text text={{{ [<__candidacy__>!has[won]then[ ]] }}} />
<$text text={{{ [<__candidacy__>get[won]match[yes]then[✅]] }}} />
<$text text={{{ [<__candidacy__>get[won]match[no]then[❌]] }}} />
<$text text={{{ [<__candidacy__>get[won]match[pending]then[◎]] }}} />
\end

in a tiddler tagged with $:/tags/Macro.

I call this from a few different templates across my wiki.

Again, this is working code. I do not need to fix it. But it feels far too redundant for what it does. I was looking for suggestions on how to fix it. There were many working suggestions. Two of the approaches suggested stand out. One, suggested by several people, but first by @etardiff, is this:

\define yes() ✅
\define no() ❌
\define pending() ◎
\function results(candidacy)
[<candidacy>get[won]getvariable[]]
\end

The other. which I hinted at the in the initial post, and which many people suggested, including @stobot and @TW_Tones is to store the configuration as data and use getindex to recover the specific value. I thought—and stobot’s suggestion was the same—that this would be stored in a data tiddler. TW_Tone’s pointed out that this could also be stored without the dedicated tiddler as a simple JSON string in a variable.

Those are the kinds of answers I was hoping for, and I learned a lot from them. But there’s one thing I haven’t been able to get right:

Open question

I’m still hoping that this can be further generalized to have a function that takes the name of the tiddler (or JSON variable) and the value to look up, for a general-purpose substitution configuration, and that we can then write specific functions that call this one, passing hard-coded references to a specific configuration.

I would hope to use the specific function like this:

<$list filter="[tag[Candidacy]]">
  <$link>{{{ [results.symbol<currentTiddler>] }}} {{!!caption}}</$link><br/>
<!--         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                            -->
</$list>

My attempt looks like this:

Generic function

\function lookup.in(table, key)
  [<table>getindex<key>] :else[<table>getindex[_default_]]
\end

Here lookup.in is the generic function, and it works properly:

With this JSON in election-symbols:

{ "_default_": " ", "yes": "✅", "no": "❌", "pending": "◎"}

This code:

{{{ [lookup.in[election-symbols],[yes]] }}}

yields the expected “

More specific wrapper

But my wrapper function does not work:

\function results.symbol(candidacy) 
  [lookup.in[election-symbols],<candidacy>get[won]]
\end

Using this function these attempts:

{{{ [results.symbol[Candidacy/1]] }}}

and

{{{ [results.symbol<currentTiddler>] }}}

both fail to return anything, even though the tiddler Candidacy/1 has a won property with the value no.

I would love to know what I’ve done wrong in that function.

An updated version of those tiddlers, which can be downloaded and dragged onto any 5.3.0+ wiki is here: UsingDataTiddler.json (6.1 KB)

Conclusion

I’ve learned a lot from this, and I hope others have as well. That was the goal, since I already had working code, and the duplication was more an annoyance than a serious flaw.

But I really hope someone might be able to tell me what’s wrong with my latter approach. I’ve tried all sorts of syntax, and feel like I’m reverting to magical thinking in my attempts. I’m guessing it’s something simple, but it could be deeper, and might reveal a big gap in my thinking.

Thank you

Thanks for everyone who has participated. I really have learned a lot.

Oh, me too. Thank you.

Yep. You want a distilled version of your working code. Others (me included) gave you more distilleries :nerd_face: which doesn’t really move you (or us) forward.

For me, I learned more about \function so thanks for that :wink:

1 Like

That “restatement” is very helpful, and I belive I have at some point built what you are asking for. I will return later to share. I belive there are either some features or qualities of functions, either not known to you or have missled you. In case it helps some quick statments of fact.

  • A function when called from inside a filter, or as a custom operator will act on the titles in its input. So depending on the titles you could say functions are operating on the current title.
  • In the example of getting the icons, we can just give a function the value yes/no/pending and it only has an output, the icon.
    • eg {{{ [all[current]get[won]show.symbol[]] }}}
    • learning how to write functions that act on each input title it something I am yet to fully come to terms with. Teaching is the best way to learn.

@TW_Tones:

As always, thank you for the help.

I definitely could do this. But it’s a change to the API I currently support and would prefer to keep. I want to pass the tiddler title and get back the relevant symbol.

As the hundreds of uses of these symbols are created by only a few templates, it would not be particularly onerous to change those templates. But it offends my sensibilities to do so. The next time I do this perhaps there could be dozens or hundreds of call sites.

The trouble is that if something causes me to change how I record the wins, I would then have to go back to every place I call this function and replace [all[current]get[won] with whatever the new mechanism happens to be. If the function itself handles this, then I can change it when needed without issue. (This by the way, is not far-fetched at all. I’m considering removing the won property and deriving this from vote counts, some office-specific configuration and the Connecticut minority party representation laws, although that may be more complexity than I would accept.)

You know, it doesn’t sound hard, but so often the devil is in the details. :wink:

Just so you know as a rule all my macros and filters with few exception operator on the current tiddler.

  • I can write what you ask, I will have to come back later.
  • I will use a function and/or procedure.

In this thread Using ChatGPT with and or about TiddlyWiki I discuss how I was able to generate new operators written in javascript using chat GPT such as [totalcost[price],[quantity]] that operates on current tiddler.

  • But we can do this in a custom operator.,

It would not bother me to call it like this:

{{{ [<currentTiddler>result.symbol[]] }}}

I just want the lookup of the won property to happen inside that function call.

1 Like

It looks like you’re essentially trying to pass a function, [<candidacy>get[won]], as the “key” parameter of the lookup.in function, right? Unfortunately, I don’t think it’s possible to feed a function like that… at least not with your current syntax. You’ve defined lookup.in with two parameters, table and key, and when using a function as a custom filter operator, parameters are specified in a comma-separated list. That means that in [lookup.in[election-symbols],<candidacy>get[won]]

  • table = election-symbols
  • key = <<candidacy>>

So as written, {{{ [results.symbol[Candidacy/1]] }}} will look for the value of the Candidacy/1 index in the election-symbols data tiddler, and then try to return the value of that (nonexistent) tiddler’s “won” field.

Luckily, we can parse a filter run and then feed it to your custom function, all in the same run:

\function results.symbol(candidacy) 
[<candidacy>get[won]] :map[lookup.in[election-symbols],<currentTiddler>]
\end

As per @etardiff simple approach before you can use a procedure to contain the name/icons and also use a function within it to generate a text only icon.

  • This is a procedure and can be used as a macro
\procedure result-symbol(fieldname:won)
   \define yes() ✅
   \define no() ❌
   \define pending() ◎
   \function get.symbol() [all[current]get<fieldname>getvariable[]]
  <<get.symbol>>
\end result-symbol

<<result-symbol>>
  • This acts on the current tiddler so if it is the candidacy tiddler, or inside a list widget will work.
  • This also allows you to provide a different fieldname with won the default

Note:

I and others have pointed out some delayed rendering of functions after a change, I need to close and open, the tiddler or preview, sometimes to see a change.

  • This has made this work fiddly at times and sometimes a change is not reflected eg; if the won field changes, unless the tiddler is refreshed.
  • This needs fixing.

Alternatively if you want to use it in a filter or as a filter operator you can ditch the procedure

   \define yes() ✅
   \define no() ❌
   \define pending() ◎
   \function get.symbol(fieldname:won) [all[current]get<fieldname>getvariable[]]
 

<<get.symbol>> on the current tiddler
{{{ [<candidacy>get.symbol[]] }}}

Not intentionally. :frowning_face: I thought I was passing the result of calling that function. Among the variations I tried was [lookup.in[election-symbols],[<candidacy>get[won]]], but that is a filter syntax error, for a reason I don’t yet understand. (I’m not asking. I will spend some time on it later this week, but that should demonstrate what I thought I was doing.) This definitely explains the problems I’ve been having.

Thank you very much. That is what I was looking for. A big fan of functional programming, I reach for map all the time, and not just for lists. But I don’t yet have the habit of using it in this way, as almost a syntactic punctuation to separate the function application from another expression. It makes sense, but I need to retrain my brain a little to see it this way.

Thank you for the response. This one was really bugging me!

Thank you. I will probably combine @etardiff’s approach with your suggestion of using a JSON string rather than a JSON tiddler. But seeing how many useful variants there are is extremely helpful.

I agree that this should be fixed. I haven’t run across it yet, but will try to keep my eye out for it.

I’ve been trying to learn my way into the core, having worked on several changes more on the periphery. This might be a good thing for me to take up, although there’s a strong chance that it’s too far beyond my current knowledge. (This thread demonstrates that my understanding of TW \functions is still fairly weak.) I don’t know if I’ll have time soon, but if I do, I might try to take this on. Do you know if there is already a GitHub issue for it?

1 Like

Me, too. It’s very hard to pin down where/why/(who). I thought it was me (usually is, it’s all about me, didn’t you know?), because I’ve been faffing around with stacks and window[“vars”] (globalThis? :shushing_face: ).

Perhaps there’s an overly zealous/eager optimisation in the core somewhere?

I think I’m good now – with help from @EricShulman’s timeout plugin (thanks Eric) I managed to force the update to happen (focus away, focus back in ~2x20ms).

1 Like

That sounds like a bug that has already been fixed for v5.3.2. It would be helpful if you could test https://tiddlywiki.com/prerelease

1 Like

A quick test where I update a field in edit mode, that is used as input to a custom operator is working well.

  • I will need a little more time to identify the cases that were causing problems because it was somewhat fragile.

But it is looking good thanks.

I am not exactly sure what this means;

This is currently just a failing test for #7696