Conditional macro

Hi ,

if i have a filter as such <$list filter=’[!is[system]tag[done]sort[title]]’>

This is followed by a macro <<macro1>> which has some data behind that it calls

is there way to make the macro call conditional depending on the output of the filter ?

so for example it would Only run if the filter finds tiddlers tagged with “done” , or perhaps even another macro would run in the case of no tiddlers were found tagged with done

<$list> does that already. The body of $list works only if the filter is successful.

<$list filter="[!is[system]tag[done]sort[title]]">
<<colour foreground>>
</$list>

Thank you ,

ok maybe i have asked the question wrong , suppose i have just the macro , without the filter, can i for example make this macro trigger or not depending on some criteria, lets say in the tags field, so if the tiddler holding the macro call is tagged with x the macro would trigger , and if not it wouldn’t

so basically If then type of thing

sorry if i am making a mess of an explanation

Macros them self do not “trigger” actions. But perhaps you are missusing the words.

A common pattern used is the list widget to conditionally display something; as @talha131 illustrated.

However inside a macro you can do the same, to show hide code according to some condition.

\define button-actions()
<$list filter="[all[current]tag[X]]">
   <$action-navigate $to="HelloThere"/>
</$list>
\end
<$button actions=<<button-actions>> >Open Hello</$button>
  • The above button always appears but if the current tiddler has the X tag it will also navigate to HelloThere.
  • Basically the <$action-navigate is hidden until the condition in “button-actions” is true.
    • The button just does nothing if not
1 Like

In addition to use the <$list> widget to iterate over a set of tiddlers, it can also be used as an “if” test.

For example, suppose you call a macro (e.g., <<myMacro>>), but you only want it to do something if the current tiddler is tagged with “someTag”, you could write:

\define myMacro()
<$list filter="[<currentTiddler>tag[someTag]]">
... rest of macro here
</$list>
\end

If the current tiddler is not tagged with “someTag”, then the output of the filter will be empty and the entire contents within in the <$list> widget will be skipped.

or, let’s suppose you want the macro to do something if ANY tiddlers are tagged with “someTag”, you could write:

\define myMacro()
<$list filter="[tag[someTag]limit[1]]">
... rest of macro here
</$list>
\end

Note the use of limit[1]. This ensures that the macro contents will only be processed once, regardless of how many tiddlers are tagged with “someTag”; and, just like in the previous example, if NO tiddlers are tagged with “someTag”, then the output of the filter will be empty and the entire contents within in the <$list> widget will be skipped.

There are many other filter runs that are useful as “if” tests. For example:

  • <$list filter="[tag[someTag]tag[otherTag]limit[1]]">
    = "if ANY tiddlers are tagged with both someTag AND otherTag"

  • <$list filter="[tag[someTag]] [tag[otherTag]] +[limit[1]">
    = "if ANY tiddlers are tagged with either someTag OR otherTag"

  • <$list filter="[tag[someTag]count[]compare:integer:ge[2]]">
    = "If there are TWO OR MORE tiddlers tagged with someTag"

enjoy,
-e

6 Likes

G’day Paul,

I had started putting this together but had to hit pause to pickup my kiddo.

You’ve gotten some good examples in the meantime, but I figured no sense letting the one I did go to waste, so…

I tossed aside the tag operator and went with this instead to try and focus on the “IF THEN” mimickery:

<$list filter="""[<now "DDD">match[Tuesday]]""">
Woohoo, today is Tuesday !
</$list>

So if now (the result of that macro, with the date formatted as “DDD” is equal to Tuesday, then for that value let’s show “Woohoo, today is Tuesday!”

Alternatively:

<$list filter="""[<now "DDD">match[Tuesday]]""">
Woohoo, today is <<currentTiddler>> !
</$list>
3 Likes

Great examples. How would you introduce an ‘else’ into the process?

<$list filter="""[<now "DDD">match[Tuesday]]""">
Woohoo, today is Tuesday !
</$list>

<$list filter="""[<now "DDD">!match[Tuesday]]""">
Oh no, today is not Tuesday!
</$list>

From my perspective: Keep in mind, these aren’t really “IF THEN” structures. These are “FOR EACH” structures. What is between the “list” start and end is repeated for each item that passes through the filter.

2 Likes

Two methods for adding an else clause:

  • Use the emptyMessage=... parameter in the $list widget. Thus:
<$list filter="[<currentTiddler>tag[someTag]]"
   emptyMessage="<<currentTiddler>> doesn't have someTag">
   <<currentTiddler>> has someTag
</$list>

Note that for readability, the then and else can be written as separate macros, like this:

\define istagged() <<currentTiddler>> has someTag
\define nottagged() <<currentTiddler>> doesn't have someTag
<$list filter="[<currentTiddler>tag[someTag]]" emptyMessage=<<nottagged>>><<istagged>></$list>

Note that in the above example, I’ve used short-form “single line” macro definitions. Of course, if the istagged() and nottagged() definitions are more complex, you could write them as “multi line” macros, like this:

\define istagged()
<<currentTiddler>> has someTag
\end

\define nottagged()
<<currentTiddler>> doesn't have someTag
\end

Alternatively, you could use then and else filter operators to first set a test variable to “yes” or “no”, and then separately check that variable’s value, like this:

<$let test={{{ [<currentTiddler>tag[someTag]then[yes]else[no]] }}}>
<$list filter="[<test>match[yes]]" emptyMessage=<<nottagged>>><<istagged>></$list>

or even like this:

<$let test={{{ [<currentTiddler>tag[someTag]then[yes]else[no]] }}}>
<$list filter="[<test>match[yes]]"><<istagged>></$list>
<$list filter="[<test>match[no]]"><<nottagged>></$list>
5 Likes

Thanks all. Textbook examples!

Thank you :slight_smile: .

I got it , and was able to achieve what i needed ,like you have explained , i used a simple filter as an argument with my actual macro underneath it , then i wrapped both the filter and my actual macro in another macro , then just used that wrap macro in other tiddlers which only give out its contents if the tiddlers were tagged with the “done” tag

Thanks again for all your help

\define wrapmacro()
<$list filter="[<currentTiddler>tag[done]]">
<<Actualmacro>>
\end

…sorry for the unnecessary dumbed down reiteration , just though might be helpful for the codding impaired like myself

2 Likes

If you want an else clause, you can do it without the <$list> widget. see example.

<$let nextStep = {{{ [<currentTiddler>prefix[who]then[yesmacro]else[elsemacro]] }}}>
   <$macrocall $name=<<nextStep>> arg="argument common to both yesmacro and elsemacro"/>
</$let>

If you dont set the else filter op, you have a simple if filter for calling the macro. Pretty simple. This method or the use of the <$list> widget is a matter of taste. Note that this matter allow the simpler way of calling the macro.

Thank you , and sorry for the late reply

ok maybe i need to add more context, i am not using a standard filter, i am using the kin filter, to find tiddlers that relate to the current tiddler i am querying by tags, then i am capturing numeric values in a field called “expense”, then adding them up .

this can be achieved as follows

<$set name=total-expense filter="[kin::to] :reduce[get[expense]add]">
<>

here is the problem, some of these tiddlers underneath, might have an additional field called “externalexpense” , this field holds one or more tiddler name(not a numeric value) ,and the idea is to

  1. use kin filter to find tiddlers with an externalexpense field
  2. capture the tiddler names in the externalexpense field, then
  3. do the same original operation against these tiddler names , find their kin , then find their “Expense” field values, sum them up and come back with a total

and hence why i was asking about how variables are stored, because the kin filter will return multiple results , which i want to store somehow, to be able query, and run additional filters against, and given my limited knowledge i am suspecting there is something i am completely missing on how to properly define /store variables

this is what i have, which is not working, its giving out wrong tottals

<$set name="getexternal-expense-total" filter="[kin:tags:to<externaltiddlers>]:reduce[get[expense]add<accumulator>]">

<$macrocall $name= getexternal-expense-total externaltiddlers={{{ [[kin:tags:to<currenttiddler>has:field[externalexpense]get[externalexpense]] }}} />

does this make any sense ?

1 Like

Given you first use “a kin filter” to find a list of tiddlers then also for each of those find the “external tiddlers”, I suggest your first filter be designed just to retrieve the full list of titles, those in the original tree and those flowing from the “external tiddlers”.

  • Once you have the full list of tiddlers use another filter to extract and count the totals from the list of tiddlers.
  • Once it is working you may try and combine the two filters into one however this is not necessary, keeping the two steps seperate makes it easier to understand and modify.

Thank you tones,

i figured it out with 2 separate kin filters like you said,
also i just realized this post wast meant to be in a different thread! but any was i got it sorted

thanks again

1 Like