Dynamic Filters

Terminology

A Dynamic Filter is a filter created at run time (render time) and is not hard coded into the WikiText (script).

A Static Filter is a filter known at the scripting time and is hard coded into the WikiText (script).

Example

  • This example uses a static filter and lists all tiddlers tagged with HelloThere
<$list filter="[tag[HelloThere]]">
...
...
  • Below example shows a simple dynamic filter. It selects the correct filter based on a user input. For demonstration purpose and simplicity I used a macro here:
\define dynafilter(userinput)
<$set name=mainFilter filter="[<__userinput__>match[1]then<noteFilter>else<journalFilter>]">
<$list filter=<<mainFilter>> ...
...
...
\end

This example gets the user input if it is equal to 1 , then it lists note tiddlers using noteFilter, in all other cases it lists journal tiddlers using journalFilter.

Questions

I wish to know what is the best practice here to dynamically set a filter based on some conditions?

References

I started writing this but then realized it uses a custom filter run prefix that I am used to, pressed for time at the moment but you could try to adapt the same pattern with core code:

It really depends on the exact use case and how often the filter will change between noteFilter and journalFilter.

If the filter will change between the two often but some list items might be in common, the following would be faster in terms of refresh performance:

<$list filter="[<__userinput__>match[1]] :then[subfilter<noteFilter>] :else[subfilter<journalFilter>]">

2 Likes

Thank you Saq, reading your codes gives a lot of ideas!
Your pattern above shows me I can ignore $set and directly make decision in filter expression in $list filter attribute (where I use the selected filter).

More cases

  • Assume a set of tiddlers tagged with $:/tags/MyFilters and contain filter expression in their tid-filter field
  • You have to use that filter and do some computation using a $list like below
.
.
<$list filter="[tag[$:/tags/MyFilters]]">
<$list filter="[subfilter{!!tid-filter}]> 
..

</$list>

Question:
How can do this multi steps operation (needs two nested widgets) using a single filter expression?

@Mohammad I will respond to this challenge, however it is also a simple example to illustrate my argument that there is no problem using nested filters and it offers additional features, for example;

<$list filter="[tag[$:/tags/MyFilters]]" variable=filter-tiddler>
<<filter-tiddler>>
<$list filter="[<filter-tiddler>subfilter{!!tid-filter}] variable=each-tiddler> 
   <$link/>
</$list>
  • The point being you can access intermediate values and use them in your solution, which is something you can’t do when building single filter solutions.
  • I expect the solution is going to be almost as efficient, possibly more so as the same work needs to be done, its just hidden in a single filter.

Back to the challenge;

  • The first thing I see is you generate a list of titles [tag[$:/tags/MyFilters]] then for each of these you extract a field value !!tid-filter (which implied currentTiddler then use this as a filter.
  • This means we want to use two different “currentTiddlers”
    • I will return with a fuller answer, However when I have this kind of problem I go lookig for operators using <<..currentTiddler>> - the input title
    • …currentTiddler - the value of the variable currentTiddler outside the filter run.

It really depends on the context of what you are trying to do as to whether there is a benefit in trying to use a single filter expression or what a good code pattern might be. If you want to use the list of results for further processing or as input somewhere else, then yes absolutely a single filter expression is desirable where possible.

Off the top of my head you could try something along these lines:

[tag[$:/tags/MyFilters]] :map:flat[subfilter{!!tid-filter}] :and[unique[]]

Drop the last run if you don’t need to deduplicate.

2 Likes

Wonderful! I just had to make a small change as below

[tag[$:/tags/MyFilters]]  :map:flat[subfilter{!!tid-filter}] :and[unique[]]

Yes, that is the usecase! I am working with some complex template! A script determine what template shall be used based on criteria comes for current tiddler, and then I need to do further computation.

One another real case is Mehregan Smart tab! When there are several smart tabs, and I want to show a notification bubble next to tab title indicating the tab is has any contents or not!

Thank you Tony! I am 100% with you!
In my case I have to pass the results to other widgets to do further processing in a complex code! So I need to keep the code simple and maintainable and remove extra parts!

But for other cases your pattern is more clear, and flexible.

:then[subfilter<noteFilter>]

Wait, what? Is this some undocumented filter run prefix or are we talking prerelease?

Have a nice day
Yaisog

1 Like

Note the preface to that comment:

Acknowledged.
Would you mind sharing your then filter run prefix code? There have been a few instances where I wished for such a thing, but had to go a more convoluted way, as it doesn’t exist in the core.

Have a nice day
Yaisog

It is at the top of my TW to do list to publish a plugin consisting of my custom filters as soon as the time allows, which hopefully should be later this month.

6 Likes

Hi @saqimtiaz

I remember this

Is it the time to have :then[] into the core? I think this filter run prefix helps writing cleaner and more understandable filters.

2 Likes

+1 from me. There were a couple of times where my filters would have been “cleaner” if I had that prefix available.

For :then see The :then Filter Run Prefix (a PR Under Review) - Discussion - Talk TW (tiddlywiki.org)