Thanks for the silent help!

There is an issue at GitHub that discusses the possibility to “store” filter-run “results” into variables, so they can be used in a later filter-run as a starting point.

[IDEA] Filter operator to save its input into a variable accessible in later filter steps #6893

or slightly related since that will make filters even more complex for (human parsers) aka other users to read. Add support for comments within filters #6847

That would go part of the way toward solving this, but I’m not sure it would get entirely there. And anything I can think of would have readability or parse-ability (?) concerns. But if I didn’t try to go down this rabbit hole, is there a simple way to construct a filter that combines the results of several previous filter runs stored as variables?

You can use the filter[] operator instead of the run prefix equivalent, though it will require the use of an extra variable:
https://tiddlywiki.com/#filter%20Operator

In general, you can isolate the execution of multiple runs in an expression by saving them as a variable and executing them using the subfilter operator or as a function:
https://tiddlywiki.com/#subfilter%20Operator:[[subfilter%20Operator]]%20[[function%20Operator]]

For example:


\procedure docs() [tag[Doc Link]] :filter[tags[]prefix[Type:]count[]match[0]]

[tag[Type: Web Page]] [subfilter<docs>] :and[sort[]]

Yes, of course. This is what I was looking for when I started with my question. Thank you. I will definitely remember that now. I probably should have had it before.

And, as I started with here, thank you for all your help in these forums!

1 Like

Could you expand on that a little bit? I have used the filter operator, but don’t quite know how I would apply it here.

The idea is to not have a separate :filter run that might then apply to the output of multiple other runs.

\procedure filterbyprefix() [tags[]prefix[Type:]count[]match[0]]

[tag[Type: Web Page]] [tag[Doc Link]filter<filterbyprefix>]  +[sort[]]

Oh, yes. That’s ideal. Thank you once again for your help!

@Scott_Sauyet the fact that I am having trouble following is my bad not yours. I have a gut feeling that there are some basic filter tricks, I can’t quite voice to help you.

If you use a filtered transclusion on a $set widget to save a list of titles in a variable, you can access that list using the set and its index parameter. In effect the name of each value is the index number.

A hunch I have is this may relate to your concept of filters (I am likely wrong)

One way I look at filter runs is the filter between two outer [ ], now you can have a set of these runs, in one filter and all results will appear in the output. As soon as you use filter run prefixes (+ ~ :filter :map) you modify how one output will relate to a subsequent filter run.

  • For example one two three is the same as [[one]] [[two]] [[three]] and same as [title[one]] [title[two]] [title[three]] is actually the same three filter runs (expressed differently, and all three will be in the output.
  • Before the resulting list is output, you can apply a filter run to all results eg +[sort[]]
  • Since filter runs are kind of linear, or a sequence, from left to right they will add to the input unless you do something in the mean time to apply another run to each item, perhaps even a conditional that the “filters out the members of the list”. Then not all members will pass through.
  • Thus if you want to treat some titles one way and others another way and the results to all come out the end you need two independent filter runs whose output ends up in the output. Keep the logic in each separate filter run. You may use a filter operator (such as filter[]) rather than :filter[...] to keep the logic all in the required filter run.

Apart from nested lists, when one set of data is within another set.

In other cases you can also use the let widget to “combine filters”. One value or list of values evaluated, is available to the next in the $let widget.

<$let list1={{{ filter }} 
      list2={{{ filter2 }}} 
      output={{{ [subfilter<list>] [subfilter<list2>] }}} <!-- this is combining the filters -->
>
<<output>> or {{{ [<output>enlist-input[]] }}}
</$let>
  • You will see I have often argued that although convenient, it is not at all necessary to combine filters into the content of a single “filter” attribute, when there are other ways to “combine them”.

I don’t know if this helps, but it is in part how I represent filters cognitively, and it may map to the way your gray matter is organised.

Post script the subfilter and now functions are other ways to “combine filters”.

Yes! This is exactly the realization I came to as I tried to formulate my original question. And this, along with the answers from @saqimtiaz, are exactly what I needed:

Thank you very much for your help and your insight.

When using “filtered transclusion” to set the value of a widget parameter, only the first item in the filter result is assigned to the parameter. Thus, if you write

<$let list1={{{ a b c }}}>

it will only return ONE item… “a”, rather than a LIST of items… “a b c”.

To store the ALL the results of a filter in a variable, you should use the $set widget, like this:

<$set name=list1 filter="a b c">
<$set name=list2 filter="d e f">
<$set name=output filter="[subfilter<list1>] [subfilter<list2>]">
LIST1 = <<list1>><br>
LIST2 = <<list2>><br>
OUTPUT = <<output>>
</$set>
</$set>
</$set>

which results in:

LIST1 = a b c
LIST2 = d e f
output = a b c d e f

-e

1 Like

I wish we could control this rather than have it enforced, especialy with the introduction of the backticks methods. That why I keep forgetting its only with $set because it seems an unnessasary limit. We could always use first[]. It would allow us to do more with list fields and lists in variables.

  • is there any other way?, if not lets make one.

Try this at tiddlywiki.com

!! How to set title-lists to variables

<$macrocall $name="wikitext-example-without-html" 
src="""\function list1() d [[e e]] f +[format:titlelist[]join[ ]]

<$text text=<<list1>>/>

<$let xx={{{ [<list1>] }}}>

Make it titles again:

{{{ [enlist<xx>]  }}}
"""/>

---

<$macrocall $name="wikitext-example-without-html" 
src="""\function list1() d [[e e]] f +[format:titlelist[]join[ ]]

2 title-lists, which can be stored in a list-field

<$let list2={{{ a [[b b]] c +[format:titlelist[]join[ ]] }}}>

{{{ [enlist<list2>][enlist<list1>] }}}

</$let>
"""/>

title-lists.json (707 Bytes)

have fun!

Joining 2 titlelists also works eg: see list3

<$macrocall $name="wikitext-example-without-html" 
src="""\function list1() d [[e e]] f +[format:titlelist[]join[ ]]

2 title-lists, which can be stored in a list-field
 
<$let list2={{{ a [[b b]] c +[format:titlelist[]join[ ]] }}}
list3={{{ [<list2>] [<list1>] +[join[ ]] }}}
>

<<list3>>

{{{ [enlist<list2>][enlist<list1>] }}}

or enlist list3

{{{ [enlist<list3>] }}}
</$let>
"""/>

I don’t have time to review today, but it looks impressive @pmario. Is there any possibility of a simple explanation of what is happening?

If we want to work with list-fields they do contain title-lists like title [[title with spaces]]

Eg: To read the list-field from HelloThere and create a link-list we can use:

{{{ [enlist{HelloThere!!list}] }}}
or
{{{ [[HelloThere]get[list]enlist-input[]] }}}

The later version is very flexible, since every element can be a variable. Eg:

\define tiddlerTitle() HelloThere
\define listName() list

{{{ [<tiddlerTitle>get<listName>enlist-input[]] }}}

[<tiddlerTitle> … defines the tiddler to be used
get<listName> … defines the name of the list-field and reads the title-list string
enlist-input[]] … converts it into an array of titles see: enlist-input docs

It will give us:

A Gentle Guide to TiddlyWiki
Discover TiddlyWiki
Some of the things you can do with TiddlyWiki
Ten reasons to switch to TiddlyWiki
Examples
What happened to the original TiddlyWiki?


If we want to dynamically create a variable that contains a title-list we need to crate a “title-list string” that can be stored in a list-field.

As Eric wrote if we assign a “filtered transclusion” to a variable we only get the first parameter. That has been a very early design decision.

\function list1() d [[e e]] f

<$let list2={{{ a [[b b]] c }}}>

If we have filtered-transclusions we need to convert them to a title-string, then it will be 1 parameter.

That’s exactly what the format and join operators do

\function list1() d [[e e]] f +[format:titlelist[]join[ ]]

<$let list2={{{ a [[b b]] c +[format:titlelist[]join[ ]] }}}

Now the variable title-list string can be used with enlist or enlist-input to be separated again

{{{ [enlist<list2>][enlist<list1>] }}}

While writing this response I also saw, that I forgot about this.
Instead of creating a string we can define one, with \procedure as follows.

\procedure list-p() d [[e e]] f

{{{ [<list-p>enlist-input[]] }}}

Hope that makes sense
-mario

  • One that persists in the new back tick subsitutions. It would be nice to have a bypass.

Thanks @pmario this is not voodo as I suspected in your examples,

To sumarise if you want a variable to contain a list, you may use titlelist to ensure spaces are allowed, and can use a “function”, you can use \define or \function to set the the variable to a list but not as a value of an attribute, perhaps the exception being a “filter”, inside the filter parameter on the set widget.

  • As someone who likes to use variables containing lists, I am being forced to use only a subset of methods.

As I did show, you can use the same methods, you can use with tiddler fields. An that is the “full set of methods” and not a subset.

I will revisit your examples, but given fields can only be given a value due to an edit or action (trigger) I still see it as a subset. I cant use the filtered transclusion or the new substitution forms, to set a variable to a list, because only the first will be used.

<$let title=HelloThere field=list my-list=`${ [<title>get<field>] }$` >
<<my-list>>
</$let>

If you want to see the title-list string you use the following code. The my-list variable is a string. So it is 1 element.

<$let title=HelloThere field=list my-list=`${ [<title>get<field>] }$` >
<$text text=<<my-list>> />
</$let>

If you want to use the variable with a list you need to split it into titles

<$let title=HelloThere field=list my-list=`${ [<title>get<field>] }$` >

<$text text=<<my-list>> />

---

<$list filter=`${ [<my-list>] }$`/>

---

<$list filter="[<my-list>enlist-input[]]"/>

</$let>

Which gives you this:

I think it’s only a matter of experimenting how the new substitution syntax can be used. I think it also is a users preference.

I personally prefer to do title concatenation the “old” way, because I think it is very flexible and simple to read.

From my point of view the first link-widget construction below is much easier to understand than the second one. Even if the second one is only 1 line, I think the first version is much more robust and much easier to maintain.

\function newTitle() [<currentTiddler>] [<now>] +[join[-]]

Old Style: <$link to=<<newTitle>>/>
<hr>
New Style: <$link to=`$(currentTiddler)$-${[<now>]}$` />

I am using similar approaches to you but looking through your examples, I see certain patterns are forced on you because,

  • when used as the values to an attribute, both the filtered transclusions and the new back tick filter substitution, only return the first value.
    • I understand why this maybe helpful, but not why as a result we have no way to assign more than only the first value to an attribute.
  • this means using these methods to set, let, vars or any widget or html attributes, they can not be used to provide a list.
  • I understand the workarounds but there is no way to override this behaviour, that returns only the first value, so a whole class of list handling and widgets is prohibited.
  • to me this is a clear limitation we should address.