Fun with soft filter parameters and run prefixes

TIL that filter run prefixes behave interesting when used in parameters that are defined as variables.

If we start with a simple example of how things normally work:

\define outer() [first[]] [nth[3]]

<$list filter="[[HelloThere]] [[About]] [[Filters]] +[subfilter<outer>join[ - ]]" />

Each filter run in outer receives the input to subfilter as input, i.e. the output will be

HelloThere - Filters

This is a very useful thing, but one needs to be aware.

Now with filter run prefixes this is different:

\define inner() [addprefix[inner-]]

\define outer() [first[]] [nth[3]] :map[subfilter<inner>]

<$list filter="[[HelloThere]] [[About]] [[Filters]] +[subfilter<outer>join[ - ]]" />

This will output

inner-HelloThere - inner-Filters

So, the filter run with the :map prefix does not receive the subfilter input, but the output of previous filter runs in outer().
If this wasn’t so, we’d need a map[] filter operator to do this kind of thing. But then again, there is no way to get :map to work on the complete subfilter input while keeping the results of the first two runs from outer.

I have only tested :map and :and / +, but I believe from experience that this is implemented consistently for all filter run prefixes

I’m also not saying this is good or bad or right or wrong, but simply that one should be aware, as this is potentially very powerful and useful.

Have a nice day
Yaisog

1 Like

@yaisog I recommend having a look at the documentation for “Filter Expression”, in particular the table towards the end that is preceded by the text “The input of a run is normally a list of all the non-shadow tiddler titles in the wiki (in no particular order). But the + prefix can change this:”

This needs updating for the :map filter run prefix but should be helpful in understanding what the input to a filter run is, and why in your first example, both filter runs in the subfilter receive the input to the subfilter.

1 Like

Yeah, if one knows what [all[]] does and how that applies to subfilters, that table formally reproduces exactly what is happening. For the normal user this content may be a bit too abstract, without examples and all. I was actually a bit surprised by what’s happening and thus wanted to illustrate it with a simple example for others like me. The forum provides a bit more space for extended examples than does the documentation.
Please just ignore it if it’s too boring for power users.

Have a nice day
Yaisog

1 Like

Not at all. In fact that particular documentation tiddler desperately needs a rewrite. Any contributions to the documentation around this topic would be very welcome.

3 Likes

Question: Can we use this approach to retrieve a list of tiddlers with two level deep of links?

Traditionally we use something like this

  • List of tiddlers: [tag[HelloThere]]
  • One level deep of links: [tag[HelloThere]links[]]
  • Two levels deep of links: [tag[HelloThere]links[]links[]]

Final filter: [tag[HelloThere]] [tag[HelloThere]links[]] [tag[HelloThere]links[]links[]]

Very clever indeed! I havent seen such use cases for subfilter, using + seems you have passed the currentTiddler to subfilter, also you did several filters run inside subfilter

So, this way you can have separate filter runs inside a subfilter and then accumulate all the result. I can use this feature to retrieve all first degree relative of a tiddler (or list of tiddlers). For example:

Example i

\define neighbors()  [tags[]] [tagging[]] [links[]] [backlinks[]]


Items: <$count filter="[[HelloThere]] :and[subfilter<neighbors>]" />
			
<$list filter="[[HelloThere]] :and[subfilter<neighbors>]"  template="$:/core/ui/ListItemTemplate"/>

Which returns

Example ii

Even one can get the title of input. No need to mention, one can pass a list of tiddlers to the subfilter

\define neighbors()  [get[title]] [tags[]] [tagging[]] [links[]] [backlinks[]]


Items: <$count filter="[tag[HelloThere]] :and[subfilter<neighbors>]" />
			
<$list filter="[tag[HelloThere]] :and[subfilter<neighbors>sort[title]]"  template="$:/core/ui/ListItemTemplate"/>


1 Like

This I just learned recently: The [all[]] filter run (without a parameter) simply returns its input. You could use that instead of [get[title]] as the first run of the neighbors subfilter to keep the list of input tiddlers also in the output. I found this to be very elegant (but also very hidden in the docs).

1 Like

Like this?

\define linker() [all[]] [links[]]

<$list filter="[[HelloThere]subfilter<linker>subfilter<linker>subfilter<linker>]" />

Each subfilter call should give you one more level of links.

1 Like

This is fine if you have a limited number of levels to “walk” but using recursion allows it to go as deep as exists.

Or

The kin operator can do this all for you including set how many levels deep you want.

\define each-other-level(filter)
<li><$link to=<<currentTiddler>> ><$text text=<<currentTiddler>>/></$link></li>
<ul>
<$list filter="$filter$">
   <<each-other-level $filter$>>
</$list>
</ul>
\end
\define first-level(filter)
<ul>
<$list filter="$filter$">
   <<each-other-level $filter$>>
</$list>
</ul>
\end

Start in TableOfContents<br>
<$tiddler tiddler="TableOfContents">

<<first-level "[is[current]tagging[]]">>

</$tiddler>

I often find we try and jam a single filter with complexity when two (or more) nested filters do better job.

I think this is true also for subfilter. This thread opens new windows of using subfilter and also :and (e.g. +) in creating shorter (but a little complex) filters.

I cannot see here filter solution!
What I am looking for is a filter to be entered into the $:/AdvancedSearch and retrieves the tiddler titles of a TOC structure with unknown depth in advance.

I agree with you, but macros are not efficient in complex situation, and you have to wikify them and store their output in an intermediate variable. That’s why I am looking for filter solution.

If we do not want the macro-output for further processing, then absolutely what you proposed is a better solution.

1 Like

Ultimately this is the problem because if it were not necessary or was able to be performed in a filter you could use a global variable. :frowning_face: constructed by any means available.

  • That is what we could use a filter operator that wikifies the result of a macro before returning its result. Then we would not need to wrap the use of a filter in a wikify widget and can use it in a filter.

I think we do not need to have [all[]] in the linker variable. What do you think?

Using @Yaisog 's excellent taggingtree filter:

[[root]taggingtree[]]

… but @Mohammad may require a masochistic technique without external dependencies !

I think without the [all[]] you’ll only get the last level of links. i.e. only the tiddlers linked from the next-to-last level, but not the tiddlers from that level themselves, which are those linked from the third-to-last level, and so on. So, it depends on what you need.

In terms of tags, the taggingtree filter gets all tiddlers from all levels below some input tiddler, and thus is equivalent to having the [all[]] operator in there.

Have a nice day
Yaisog

1 Like

Well I always look for a WikiText solution at first place :wink:

3 Likes

Ah yes, exactly! The [all[]] or [get[title]] keep the first level and the input in other recalling…
I think the [all[]] is faster here than [get[title]]

1 Like