Thanks for the silent help!

I just want to offer a group thank you for the help you didn’t even know you – yes, you! – were giving me.

On a half-dozen occasions in the last two weeks I got stuck in something more complex than I knew how to handle. I would struggle with these on my own for a while, and consult tiddlywiki.com and search Google for references. But eventually, when I got really stuck, or really frustrated, I started writing up questions. As I’ve spent a lot of time answering questions in technical forums, I think I’m pretty good at doing the right kind of research before asking, at posting just enough details to make it possible to understand my scenario, and at explaining the alternatives I’ve considered.

In doing so lately, I find myself asking, “What would @EricShulman tell me here?”, “How would @pmario handle this?”, “Would @saqimtiaz have a better approach?” And lo and behold, I have a solution!

But when this isn’t enough, I can continue to ask if @Mohammad has already solved this, probably much better than I would, if @Charlie_Veniot did it with tools available in TW’s medieval days1, if @TW_Tones can explain how this is part of a much larger picture, if @Maurycy has a bargain for me on a plugin I just can’t live without.

Or I can just call on the helpful spirit and knowledge of Mark_S, TiddlyTweeter, twMat, telumire, Springer, etardiff, CodaCoder, and of you, my friend. You, specifically, have always been a great help.

So thank you to @jeremyruston for creating this wonderful tool, and to all those helpful people who have made this a wonderful community, especially you!

… I am not done asking questions. I’m sure I’ll be looking for help for a long time to come. But all of you have made it easier and easier to answer more of them myself. While I won’t go into the background of the question I was asking, here’s the latest answer all you wonderful folks hanging around in my head gave me (suggestions for improvement are always welcome, especially for a simpler version of count[]compare:number:eq[0], but I’m reasonably happy with this.):

tags: $:/tags/ViewTemplate

<$list filter="[<currentTiddler>tag[Doc Type]" variable="_">
<h2>Documents</h2>

<$list filter="[<currentTiddler>!match[Type: Web Page]]">
<<list-links filter:"[all[tiddlers]tag<currentTiddler>]" >>
</$list>

<$list filter="[<currentTiddler>match[Type: Web Page]]">
<<list-links filter:"[tag[Doc Link]] :filter[tags[]prefix[Type:]count[]compare:number:eq[0]] [all[tiddlers]tag[Type: Web Page]] +[sort[]]" >>
</$list>

</$list>

I’m very grateful. Thank you all!


1 note: nothing but respect meant here; I’m impressed and awed by Charlie’s skills and find his advice among the most helpful around.

10 Likes

Speaking for myself you are very welcome, you also add fresh ideas and challenges, a virtuous circle really.

  • Was this discussed elsewhere?
  • Do you mean if 0 items? do this?
    • If so it is common to use filter="filter" emptyMessage=" when nothing from filter" where emptyMessage=<<macroname>> is the most common.
\define not-web-page()  <<list-links filter:"[all[tiddlers]tag<currentTiddler>]" >>

<$list filter="[<currentTiddler>tag[Doc Type]" variable="_">
<h2>Documents</h2>


<$list filter="[<currentTiddler>match[Type: Web Page]]"
  emptyMessage="""<<list-links filter:"[all[tiddlers]tag<currentTiddler>]" >>""">
<<list-links filter:"[tag[Doc Link]] :filter[tags[]prefix[Type:]count[]compare:number:eq[0]] [all[tiddlers]tag[Type: Web Page]] +[sort[]]" >>
</$list>

<hr>
Or better
<$list filter="[<currentTiddler>match[Type: Web Page]]"  emptyMessage=<<not-web-page>> >
<<list-links filter:"[tag[Doc Link]] :filter[tags[]prefix[Type:]count[]compare:number:eq[0]] [all[tiddlers]tag[Type: Web Page]] +[sort[]]" >>
</$list>

</$list>
  • Above should work, not the use of """ because it contains "

count[]match[0]

enjoy,
-e

Thank you. That is definitely an improvement. I still yearn for an isEmpty filter. I’m sure I could write one, but it feels core-ish to me.

@Scott_Sauyet we already have this, but since you are not providing the full filter, I can’t show an example. Or what the reason for the test is, only to do when count is zero?, but when it’s zero, there is none.

  • Not to mention else

Not by me, but it seems an obvious thing to want: an isEmpty filter. Eric gives a shorter version, and might be what I go with. But this is another one of those teaching moments, and I thank you:

I know I’ve seen emptyMessage, but this is the first time I have a feel for it. Thank you… as always!

1 Like

I must :sleeping: but also consider that

  • When filter has no results, count would =0 that if there are no cases you can just say “else” to give a different outcome, either the else operator or else filter run.
  • Also we do have the is[blank] and !is[blank] for the empty string version of isEmpty

I’m not sure what you mean here. The quoted code is the entire tiddler, tagged $:/tags/ViewTemplate.

The idea is that there are a number of tags of the format Type: Excel, Type: Video, Type: Powerpoint, or Type: Web Page. I also have a large number of tiddlers tagged Doc Link. Those ones may have one of these Type: * tags. On the Type: Excel tiddler, I want to list all the tiddlers that have Type: Excel. (More explicitly, I want all those that are also tagged Doc Link, but no other tiddlers will have one of these Type: * tags.) But Type: Web Page is the default, covering 75%+ of the Doc Link tiddlers, and I don’t want to have to add that tag every time, since I can just assume it. Still, I want the Type Web Page tiddler to list all of these as well as the explicitly tagged ones.

I wrote this first as two lists, one with filter:"[tag[Doc Link]] :filter[tags[]prefix[Type:]count[]match[0]]", and the second with filter: [all[tiddlers]tag[Type: Web Page]]. This wasn’t horrible, but they were two distinct lists, so couldn’t be sorted together. I originally started my latest question to ask how to to turn all the complexity, including the above and the !match[Type: Web Page] bit into a single filter.

As I started writing that up, I realized that someone here would probably tell me to split this up into match[Type: Web Page] and !match[Type: Web Page], and that led me to a much more simple and maintainable version. That, of course, was what triggered this thread. I’ve never heard Eric Shulman’s voice, but in my head, this was still in his voice. It made me realize once again how much I’ve been learning from all of you.

Yes, that was part of what I was originally going to ask. When the filter expression has several filter runs, as with [tag[Doc Link]] :filter[tags[]prefix[Type:]count[]match[0]] , I don’t know how to combine this in toto with other parts using else or some such. But my realization about the top-level split meant that this was something I could do. That does leave me with at least one open question that was the original focus of the question I was planning on asking. I’ll mention it here in case people feel like explaining, but I have what feels like a good solution now, so it’s not necessary to solve my outstanding problem.

That question comes from this:

A filter run prefix associates that run with the entirely of what’s come before. In this:

[tag[Doc Link]] :filter[tags[]prefix[Type:]count[]match[0]] [tag[Type: Web Page]] +[sort[]]

the :filter is associated with tag[Doc Link]. That works fine in this case, because I can later add [tag[Type: Web Page]], and further add the sort to handle the combination.

But this version won’t work:

[tag[Type: Web Page]] [tag[Doc Link]] :filter[tags[]prefix[Type:]count[]match[0]] +[sort[]]

because the :filter also applies to [tag[Type: Web Page]], which it shouldn’t. For my current case, this is fine, because I can just use the first version. But if I also needed a separate :filter for [tag[Type: Web Page]], I can’t see a way to group together only certain filter runs.

So the question is: is there a way to bundle something like the :filter together with some associated filter runs, but not with the entirety of the preceding expression?

Logically, what I would want would be something like this:

[tag[Type: Web Page]] ( [tag[Doc Link]] :filter[tags[]prefix[Type:]count[]match[0]] ) +[sort[]]
<!--                  ^                                                             ^  -->

But that is not useful syntax. The parentheses are merely considered additional filter runs.

Is there syntax that does this combination? Are there useful techniques to simply combine separate filter expressions into one? Something else?


Damn it, Tones, you got me asking the question I started by saying I’d figured out from the forum voices in my head! :slight_smile:

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!