How do I create ternary conditional text?

As I build a large documentation wiki (behind corporate walls, so can’t share, sorry), I’m tracking the status of my Tiddlers: complete (-ish, of course!), entirely missing, and tagged TODO. I have something that works all right, but would be improved.

I list my titles in a table, with a column that shows status of a checkmark or “todo”. A separate, smaller list simply shows the missing titles. That works fine, but it would be better if I could find a way to show either of three states in that column: ‘missing’, ‘todo’, or a checkbox if neither is true.

Is there a straightforward way to extend this:

<$text text={{{ [<currentTiddler>tag[TODO]then[todo]else[✓]] }}}/>

to include the text “missing” if the title is missing?

For context, it’s running in here:

<$list filter="[all[tiddlers]!is[system]sort[]]">
  <tr>
    <td><$link/></td>
    <td><$text text={{{ [<currentTiddler>tag[TODO]then[todo]else[✓]] }}}/></td>
  </tr>
</$list>

I know I would have to change the $list filter to start with [all[tiddler+missing]]. But how do I extend the then-else logic to a third case? I want to interject something like is[missing]then[missing], but I’m not sure how it can be combined.

Any suggestions?

Even more context

This is the complete tiddler:

caption: Status
created: 20221215211815574
modified: 20221216151538049
tags: $:/tags/MoreSideBar
title: $:/_/Status
type: text/vnd.tiddlywiki

<$list filter="[all[missing]first[]]">
''Missing''
<<list-links filter:[all[missing]]>>
<hr/>
</$list>

''Complete <$text text={{{ [all[]!tag[TODO]count[]] }}}/>, 
  TODO: <$text text={{{ [tag[TODO]count[]] }}}/>,
  Missing: <$text text={{{ [all[missing]count[]] }}}/>''

<table>
  <tr><th>Title</th><th>Status</th></tr>
<$list filter="[all[tiddlers]!is[system]sort[]]">
  <tr>
    <td><$link/></td>
    <td><$text text={{{ [<currentTiddler>tag[TODO]then[todo]else[✓]] }}}/></td>
  </tr>
</$list>
</table>

Try this:
{{{ [<currentTiddler>tag[TODO]then[todo]else<currentTiddler>is[missing]then[missing]else[✓]] }}}

Note that for readability, you can also write this as three separate filter runs, like this:
{{{ [<currentTiddler>tag[TODO]then[todo]] ~[<currentTiddler>is[missing]then[missing]] ~[[✓]] }}}
by doing so, it enables you to put each condition on a separate line, like this:

{{{ [<currentTiddler>tag[TODO]then[todo]]
   ~[<currentTiddler>is[missing]then[missing]]
   ~[[✓]] }}}
2 Likes

Thank you. This solved it. Now I have to go investigate the tilde. I hadn’t seen that yet.

This solution did not work. I’m not sure why, but wherever it was supposed to report “todo”, it instead had “missing”. I think I had tried something like it in my attempts. I’d love to know what’s wrong with it, because it looks right to my inexperienced eyes.

The tilde is a compact form of the :else[...] filter run prefix.

The problem with the second “solution” is sort of like “order of operations and implied parentheses” in arithmetic.
It doesn’t work as expected because of an interaction between the “then” and “else” that occur in the same filter run, which causes that part of the filter run to be evaluated as:

[todo]else<currentTiddler>is[missing]then[missing]

and, since [todo] is a literal string constant, [todo]else<currentTiddler> always evaluates to [todo], which is not a tiddler, and thus is[missing] is TRUE, resulting in [missing] as the filter result.

I’ve been bitten by this problem many times, and even with my extensive experience with TiddlyWiki filter syntax, I still make this mistake more often than I like to admit! By using separate filter runs with the :else (or ~) filter run prefix, it’s like using explicit parentheses in arithmetic to ensure the correct order of operations.

The “is” operator is meant to start off a filter run, putting it in the middle of expression causes the expression to ignore everything before it. Even if a tiddler matches to Todo, it is then ignored as the filtering starts over when you reach the “is” portion.

The “~” can be thought of as an “or, this…” filter run connector.

I think of it as: if the previous run doesn’t match anything, try this…!

You can string together multiple runs, like what Eric did, without getting too complex. Each filter run is evaluated from first to last, and stops when there is a match.

Edit: don’t mind me, just follow what Eric posted before me

There are two main types of filter operators: “selection constructors” and “selection modifiers”. Your description of handling for the is[] operator is backwards. What you are describing applies to the all[] operator, which is a “selection constructor” and ignores any preceding filter output. In contrast, the is[] operator is a “selection modifier” and DOES process any preceding filter output.

see https://tiddlywiki.com/#Selection%20Constructors. Also, to get a list of all selection constructors, see https://tiddlywiki.com/#all%20Operator, and then click on the “selection constructors” tag.

Thank you. I had found the tilde at https://tiddlywiki.com/#Filter%20Expression, and it makes sense to me. What I hadn’t followed was what I might think of as the binding of then and else. From programming language experience, I was thinking [[condition]then[consequent]else[alternative]] is the equivalent of

if condition
  then return consequent
  else return alternative

where the then and else are bound to the boolean value of condition.

If I understand correctly what you and @Brian_Radspinner are saying, it’s more like

if (condition)
  result ← consequent // then[consequent]
if (result is empty)  // else[alternative]
  result ← alternative
return result

And since consequent here is ‘todo’, which is not empty, we never process the else. Is that close? (Yes, I know that “return” here isn’t quite right, as it just feeds to the next step, but I hope the parallel is clear.)

I think that matches.

Thank you both for your help!

The issue here is I believe as much as to do with the concept of default handling, what to do where something is NOT the default. Or what to do when it IS something, now introduce a third state eg Missing the logic can start to get messy.

  • In this Topic you ask a quite specific question and get a quite specific answer.
  • In my view the Question and answer can be generalised further and provide a broader solution.

However in most cases you can turn your logic around to ask the question in a more appropriate way and its gets simpler.

It would be easier for me to give a simple illustration with your example if you published a table of conditions and the desired outcomes, rather than making us first reverse engineer your logic so we can then engineer a solution.

  • I have tried a few times to build a “condition table tool” on TiddlyWIki to help with these kind of design requirements. I may revisit it again.

Tiddlers “variables” are these correct?

  • Scope of all tiddler (titles) to be considered [all[tiddlers+missing]]
  • Has todo tag - indicate its a todo item
  • Is a missing tiddler - Indicate its a missing tiddler (but why?)
  • Is neither missing or todo - Provide a checkbox (to do what?)
    • Perhaps this should be rephased “does not have a todo tag” because missing tiddlers can’t have tags.

I have at least half a dozen ways to simplify the way we describe the “problem” and subsequently solve the “problem”, if I were more confident of your requirement.

  • You may be very surprised at the possibilities.

Finally this may not be an appropriate use of the missing tiddlers and perhaps this can be handled a different way altogether.

Hmm, I thought the question was pretty self-explanatory. I’ve posted a wiki showing how this is being used at Current state demo — Demo for talk.tiddlywiki thread 5494.

I’d love to hear alternative suggestions to either the implementation or the fundamental design.

Perhaps you can confirm?

Never mind. Your example is enough. Your workflow is a little unusual for me but now I understand.

I will be back.

Tiddlers are object, they have properties (fields), property values (field value) and can have methods (macro, WikiText scripts → functions)