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

It sure does!

Not at all! Thank you very much for your help.

These are both nice improvements on my original code. But I think @etardiff’s version is overall the simplest.

I can see the value in that for other uses. For this one, it’s just a distraction.

That trips me up all the time!

It should be possible. I’ll try to look soon.

2 Likes

Yes, I could implement the same reduction @etardiff did into my solution as well. By using a getvariable as a result if the goal is to reduce as much as possible, but my approach is to abstract, generalise and self document, especialy the second (short.status) example.

For a simple solution Emily’s is the best.

Further to @etardiff’s solution.

  • FYI in @etardiff’s solution the defines can be replaces with a \function or \procedure without change in behaviour
  • My desire to explore functions and abstract leads me to observe;

We could define a set of “unicode” icons for any known value yes, no, maybe, true/false, on/of show/hide, pending etc…

I may share this extended discussion in its own topic

1 Like

Just for fun, here’s a genericized approach that lets you specify a field to check, a list of possible options and the desired output for each, and a fall-back option to use if the field is blank:

\procedure conditional-display(field, options, blank)
<$setmultiplevariables
	$names="[<options>split[; ]] :map[split[: ]nth[1]]"
	$values="[<options>split[; ]] :map[split[: ]nth[2]]">
<$transclude $variable={{{ [<currentTiddler>get<field>] ~blank }}} />
</$setmultiplevariables>
\end

<<conditional-display "won" options:"yes: ✅; no: ❌; pending: ◎" blank:"something else!">>

In this case, since we’re constructing variables on the fly and using using $transclude rather than $text to display them, you can use wikitext in a “definition” and it will be properly wikified for display. So, for instance, this would also work:

<<conditional-display "won" options:"yes: ✅; no: {{{ [tag[HelloThere]] }}}; pending: ◎" blank:"[[HelloThere]]">>

I chose to use : to separate each value and its output, as a dictionary tiddler does, and ; to separate each value pair in the list—but you could change them to the delimiters of your choice. If we wanted to put each pair on its own line, for example:

\procedure conditional-display(field, options, blank)
<$setmultiplevariables
	$names="[<options>splitregexp[\n]] :map[split[: ]nth[1]]"
	$values="[<options>splitregexp[\n]] :map[split[: ]nth[2]]">
<$transclude $variable={{{ [<currentTiddler>get<field>] ~blank }}} />
</$setmultiplevariables>
\end

<<conditional-display 
	field:"won"
	options:"yes: ✅
no: {{{ [tag[HelloThere]] }}}
pending: here's some longer text that could benefit from its own line"
	blank:"[[HelloThere]]">>
1 Like

This is a good point! In this case, I used \define rather than one of the more “modern” alternatives because 1) it’s slightly shorter, and 2) it highlights the fact that we’re defining variables to be used by the function.

I have used that too :nerd_face:

@etardiff some interesting ideas in your conditional-display, it expands in my mind what can be passed into a macro. I too have exploered this further but its better I stop exploring this path for now. I wont start another topic. So here is my somewhat final example;

\widget $value.icon(field:"fieldname" tiddler tiddlervar:"currentTiddler") 
   \function yes() ✅
   \function no() ❌
   \function pending() ◎
   \function tiddlername() [<tiddler>!is[blank]] :else[<tiddlervar>getvariable[]] 
   \function fieldvalue() [<tiddlername>get<field>] 
   \function value.icon()  [<fieldvalue>getvariable[]!is[blank]] :else[<fieldvalue>]
    <span title={{{ [<field>] [[has value:]] [<fieldvalue>] +[join[ ]] }}}><<value.icon>></span>
\end $value.icon

# <$let currentTitle="value icons with suffix"><$value.icon tiddlervar="currentTitle" /></$let>
# <$value.icon/>
# <$value.icon field=status/>
# <$value.icon tiddler="value icons with suffix" />
# <$value.icon field=status tiddler="value icons with suffix" />

Replace the tiddler names and fields with ones relavant to your own test data

  • Using the get variable we can actualy pass in a tiddlervar to use, otherise it uses currentTiddler or tiddlername (this is a great feature to use elsewhere)
  • We can use simple names for the value icons because they are only known within this widget, Keep It Simple Sweetheart (principal)
  • Widgets are good if the only thing you need from them is to display the result, as is the case, with displaying icons.
  • For any value you use you can come to the widget and give that value an icon and just use the same widget.
  • There is a tooltip for each icon, you could add the tiddlername as well.
  • If there is no matching icon the value will be displayed, so you can find an icon later if desired.

(Update: added a “bad data scenario” with a :bomb: and a “no value scenario” with a :question:)

Potential internals for a macro/function:

<$let won_options="yes:✅, no:❌, pending:◎, unknown:❓"
         won_val={{{ [[candidacy]get[won]else[unknown]] }}}>

<$text text={{{ [<won_options>split[,]regexp<won_val>split[:]nth[2]else[💣]] }}} />

</$let>
3 Likes

You could also use cascade prefix. Only you need have stored your filters to call it. I read in documentatio the use with append, it is not simpler. I found a more simple version of cascade with append filter, but you have to store al filter together.

My dummy test :

\define won() 2

\define filters() [match[1]then[A]] [match[2]then[B]]

{{{ [<won>]  :cascade[[]append<filters>] }}}

Maybe the longer version is more readable. but if you have many filter, you have to add n-1 time append operator.

\define won() 2

\define filter1() [match[1]then[A]] 
\define filter2() [match[2]then[B]]

{{{ [<won>]  :cascade[<filter1>append<filter2>] }}}
2 Likes

Not exactly what you were asking for, but if you have a lot of these value lookups you could store them in a datatiddler and use getindex[].

Yes, I mentioned that in the OP, and was trying that right now, getting stuck. I’ll post another version in a few minutes asking how to fix my approach.

I’ve been playing around with these. I think I agree with @stobot simply because it’s all too “wordy”. I did, however, have a brain-fart for a new operator…

Instead of…

get[won]match[yes]then[✅]get[won]match[no]then[❌]

use…

where[won],[yes],[✅],[❌]

EDIT: oops…

where{won},[yes],[✅],[❌]

1 Like

I’m curious as to why the following alternative does not work. I’m almost certain it’s a simple syntax error, but I can’t find it. You can test it with this:

UsingDataTiddler.json (5.5 KB)

Any ideas?

Definitions

\function lookup.in(table, key) [<table>getindex<key>] :else[<table>getindex[_default_]]
\function results.symbol(candidacy) [lookup.in[$:/_/adtc/lookups/election-wins],<candidacy>get[won]]

Content of $:/_/adtc/lookups/election-wins

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

Sample Candidacy

caption: 1stSel - Adrian Mandeville (R)
contest: 2023-General
created: 20230821202714539
modified: 20231110203113532
name: Adrian Mandeville
organization: First Selectman
party: Republican
revision: 0
tags: Candidacy
title: Candidacy/1
type: text/vnd.tiddlywiki
votes: 392
won: no

lookup.in works as expected

Wikitext

* "{{{ [lookup.in[$:/_/adtc/lookups/election-wins],[yes]] }}}"
* "{{{ [lookup.in[$:/_/adtc/lookups/election-wins],[no]] }}}"
* "{{{ [lookup.in[$:/_/adtc/lookups/election-wins],[pending]] }}}"
* "{{{ [lookup.in[$:/_/adtc/lookups/election-wins],[]] }}}"

Results

  • :white_check_mark:
  • :x:
  • " "

But results.symbol does not

Wikitext

* "{{{ [results.symbol[Candidacy/1]] }}}"
* "<$list filter="[[Candidacy/1]]">{{{ [results.symbol<currentTiddler>] }}}</$list>"

Results

  • “”
  • “”
1 Like

Nice! Maybe even more useful:

where{won},[yes ✅],[no ❌],[pending ◎],[ ]

The final case would be a default value, if none of the others match.

But I don’t know if we have any variable-length operators like this.

1 Like

No. I realized immediately it doesn’t scale. But for simple binary/ternary selections…

Offline I was exploring custom widgets and realised if you had a json array in a variable containing the value and icon pairs you could avoid the seperate data tiddler.

This prompted me to make a simple custom widget you give parameter value pairs to and it displays the result in a number of different formats including a json array. You can then copy it and set a variable to the array. Easier and safer than manualy writing it or having to create a data tiddler.

@Scott_Sauyet I will share my json data variable, using the above, later today. I am heading off on a bush walk.:sunglasses:

1 Like

I have a feeling I’m missing a key part of what you’re trying to solve, but this is where my mind was when I suggested it. A “responses” dictionary tiddler and then using the getindex[] call to grab it. I’m showing 3 different options here, one without functions using map to get the value into the getindex[], a function where you pass it the value to lookup - “won” field, and lastly a function baking in that it’ll always be the won field of the tiddler passed to it.

tiddlers.json (692 Bytes)

also
image

1 Like

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!