Add Extra User Defined Filter Run to Other Predefined Filter Runs

Assume I have a shadow tiddler where user is not recommended to touch it. For example

<$list filter="[tag[Learning]]">

</$list>

I wish to pass an extra user defined filter (stored in a tiddler or variable) to further process the first filter. The simple solution is to use subfilter, so if the original code is written like below

<$list filter="[tag[Learning]]  :and[subfilter<user-filter>]"  >

</$list>

This works fine, but fails if user-filter is not defined. One work around is to use defualt value like [all[]] but I am looking for better solution.

In summary: how can I add a user defined filter run (may have many step) to a predefined filter?

Note: The above example can be tested on tiddlywiki.ocm

download User-Defined-Filter-Extra-Filter.json (392 Bytes)
and open Test My-Template, try to change the user-filter

An old school solution is to wrap the code inside a macro and pass extra filter like below

\define test-extra-filter(user-filter)
<$list filter="[tag[Learning]] $user-filter$"  >

</$list>
\end

This way, user-filter can be undefined. But I consider this a bad practice.

You can also use below macro which is better than above

\define test-extra-filter(user-filter:"[all[]]")
<$list filter="[tag[Learning]]  :and[subfilter<__user-filter__>]"  >

</$list>
\end

BUT, what we can do without using a macro?

Hi @Mohammad ,

Since we can’t directly provide the else operator with a [subfilter[]] operand, we can reverse the logic thanks to the map:flat operator:

<$let user-filter="[all[]prefix[How]]" default-filter="[all[]]">
<$list filter="[<user-filter>!is[blank]else<default-filter>] 
    :map:flat[all[tiddlers+shadows]tag[Learning]subfilter<currentTiddler>]"  >

</$list>
</$let>

First we decide which filter will be used, then we map all the input tiddlers on the chosen filter.

Or maybe more generic:

<$let user-input=    "[tag[Learning]]" 
      user-filter=   "[all[]prefix[How]]" 
      default-filter="[all[]]">
<$list filter="[<user-filter>!is[blank]else<default-filter>] 
      :map:flat[all[tiddlers+shadows]subfilter<user-input>subfilter<currentTiddler>]" >

</$list>
</$let>
2 Likes

As @xcazin had pointed you would need to wrap your list inside of the declaration of the variable user-filter.

In this cases $set widget is useful. In your example, it would be something like:

<$set name="user-filter" filter="[{filterTiddler}]" value={{filterTiddler}} emptyValue="[]">
<$list filter="[tag[Learning]]  :and[subfilter<user-filter>]"  >

</$list>
</$set>

Where filterTiddler would be the tiddler that stores the filter expression.

2 Likes

Thank you @xcazin. Both works specially for cases I want where a modifier operator is used.

1 Like

Thank you @Alvaro.

I just modified the filter="[{filterTiddler}]" to filter="[subfilter{filterTiddler}]"

Your solution works and it can accept more complex filter runs as it uses a separate run through :and

One cavet is with :and it forces its input as final output! so, if user gonna filter the selection (here [tag[Learning]] he/she should take care to not produce extra titles through filterTiddler.

:thinking: Why? The filter is only for check that the filter outpot isn’t empty. Then the filter expression will be used has paramenter of subfilter operator.

The reason is to have code working when filterTiddler is missing or filterTiddler is empty!

1 Like

I think this also should be emptyValue="[all[]]" to pass the previous result intact when nothing is specified by user!

1 Like

My mistake, I didn’t specificy correctly the doubt. The doubt is around of use of subfilter in filter="[subfilter{filterTiddler}] but now I see it the same that a tricky version of [[filterTiddler]!is[missing]!is[blank]]

1 Like