Need a variable value of nested/complex filter

I should know how to do this… but no! I need to have a variable containing a complete list of tiddlers from something like this:

<$list filter="[tag[event]contains:people{!!title}has:field[event]get[event]enlist-input[]] +[unique[]]">
<$set name=mylist filter="[tag[event]contains:event{!!title}]">
<<mylist>>
</$set>
</$list>

I’d like to remove the list widget with a single set widget with mylist containing the complete list of tiddlers.

I have tried various filter operators without success. If you need more details: This would be used on person tiddler. The list widget returns events for this person that are “parent” events (ie. these events have children events). The set filter returns all child events… so I’m my objective is to set mylist to a complete list of event tiddlers that are children events of the person tiddler. I need to use this variable in a call to a macro, so I need the list widget removed. Hope this makes sense.

Thanks,

Craig

The filter in the list uses {!!title} or the effectively current tiddler when the list is displayed, is this correct? I understand it is on person tiddlers.

  • Perhaps the output of your first list could use format:titlelist[]? not enlist-input[]] +[unique[]]
  • has:field[event]get[event] can be simplified to get[event]
  • Your set filter starts with all tiddlers again with the event tag, when inside the list is iterated once for each event found by the list filter. Perhaps use variable=event-tiddler (on the list) and in the set use <event-tiddler>.
  • Once you have it working then share back and we can suggest how to eliminate the list f necessary (but why?)

Try this, it will need TiddlyWiki 5.2.3:


[tag[event]contains:people<currentTiddler>has:field[event]get[event]enlist-input[]unique[]]
 :map:flat[all[tiddlers]tag[event]contains:event<currentTiddler>]

  • The first filter run is unchanged and returns a list of the events for the person that are parent events.
  • The second filter run uses a :map prefix and thus transforms each input title from the previous run.
    • the flat suffix for :map means we replace each input title with all the results of the map filter run and not only the first result which is the default behaviour of :map.
    • the :map filter run is evaluated once for each input title and sets the value of the currentTiddler to the title being evaluated, making it easy to check all events to see if they contain the event indicated in currentTiddler in the event field.
5 Likes

Can’t we just use: get[event]? as get operator only works for non-empty field.

Hi Mohammad, in order to keep the changes in the filter easier to understand, I tried to change only what was strictly necessary and tried to emulate the precise logic of the combination of the $list and $set widget to illustrate how they could be mapped on to a single filter run.

1 Like

This also does not work for me! but below works

:map:flat[all[tiddlers]tag[event]contains:event<currentTiddler>]

Sorry for asking too many questions, you have already explained this for me! but I cannot find the thread!

1 Like

Good spot! This is because the input to :map is a single tiddler so we need to specify all[tiddlers] if we want to select from all the tiddlers. Updating the answer above.

1 Like

Thank you Saq!
I like your clever solutions! Specially when you propose a single filter expression while I have to do it in several steps including many $list :wink:

When you simply need to print out the results on screen, combining several widgets is usually quite OK. However, when you need to use the output as input to another widget or filter, then it is recommended and helpful to use a single filter expression where possible. Otherwise you fall into the trap of needing $wikify which is an antipattern for such use cases and leads to poorer performance.

Also just for clarity please note that the proposed answer above is a single filter expression but consists of two filter runs.

1 Like

Thank you for explanation Saq!

I like your filter solutions and collect and document in TW-Scripts for my future references!

Can we summarize your question as below?

How return a list of child events for a person attends in a parent event?

@saqimtiaz @Mohammad Thank you for walking through this to find the solution. I discovered that each filter’s syntax was not exactly what I wanted. However, this answers my question. The power of this map operator is brilliant. This opens up opportunities for numerous situations. Thank you again.

Craig

Yes. That is what this does, but in fact, I want to return child events for a person where they are also referenced in the parent event.

Also, I’m using this on a tab tiddler that gets implemented on person tiddler via a template. Therefore, I couldn’t use the currentTiddler reference.

[tag[event]contains:people{!!title}get[event]enlist-input[]contains:people{!!title}unique[]]
 :map:flat[all[tiddlers]tag[event]contains:event<currentTiddler>]

The reason for:

has:field[event]

…which I removed in this example, is me thinking (likely incorrectly) that removing all tiddlers without the event field would be faster than it performing get[event]enlist-input[] when there is no event field.

1 Like

You can use! Normally it is recommended to use a template here. Look at the last paragraph in official doc tiddler: https://tiddlywiki.com/#tabs%20Macro

I believe [get[event]] is enough. If a tiddler has not an event field or its event field is empty will be ignored. The performance is Saq expertise. Hopefully he gives his advises.

1 Like

I stand corrected. Thanks. I’m not sure what occurred when I first implemented this filter. But when I changed it to {!!title} it worked. I shall put <currentTiddler> back in place.

Lets demystify this:

  1. there are tiddlers tagged event (called parent event)
  2. a parent event has these two fields
    2.1. event
    2.2. people
  3. a child event is an event in the field event of parent event
  4. the OP asks for listing all children events for a person attends in a parent event
1 Like

This gets complex real quick… and it makes my head spin.

  • There are event tiddlers tagged with event.
  • There are person tiddlers tagged with person.
  • All event tiddlers (parent and child) support an event field (title list of parent events) and a people field (a list of person tiddlers associated with the event).
  • Any event can reference another event as it’s parent with the event field. i.e. parent and child events use the same “event” tiddler definition.

Example:

World War I – this is an event. It would be a parent event for numerous child events. On the event tiddler I want to show a timeline of events that occurred during the war. In this example, I would not likely reference all people in my TW that took part in this parent event (but I could). This event has an empty event field.

The first child event could be “Archduke Francis Ferdinand is assassinated”. Its event field references the World War I event. This event can reference 1 or more parent events. It can also be a parent event to other events.

In this query, I want a list of all children event tiddlers for a person where they are referenced in both the child and parent event.

The reason for the request is for a more complex implementation. The event tab (shown on person tiddler) displays a timeline ($:/plugins/kookma/timelines) of events for that event. This plugin does not support, natively, nested timelines. But, that does not mean it cannot do it with the appropriate timeline templates. When the parent event appears on the timeline I do not want the children events tiddlers from that parent to appear in the outer timeline, because they appear nested in a sub-timeline under the parent. So, I’m using the filter provided here in a much bigger filter. In my example, I simplified the filter to obtain tiddlers, but in fact, what I am doing is removing tiddlers from my grand filter so these child events do not appear in a given level of the timeline. Now that I have my list of event tiddlers I use mylist variable like this:

[remove<mylist>]

…embedded in my grand filter. I have not tried, but instead of using a set variable could I embed the filter like this, I am guessing this would not work:

[remove[tag[event]contains:people{!!title}get[event]enlist-input[]contains:people{!!title}unique[]]
 :map:flat[all[tiddlers]tag[event]contains:event<currentTiddler>]]

Aside, but you asked, I also have an “event-role” tiddler. These are tagged “event-role” and are used as an intersection (an old DB design term–I’m old) between person and event tiddlers. These tiddlers define a person’s role in an event. So, instead of a person being referenced in the people field on an event tiddler, they are referenced in the people field of an event-role. Therefore, event role tiddlers have an event field, a people field, and a role field. In this way, I can assign an individual a role in the event.

Using the example above (which is not a great example), I would assign the person tiddler, “Francis Ferdinand” tiddler, to the “Archduke Francis Ferdinand is assassinated” event. But, I could create an event-role tiddler defining the role of “assassin” and associate that with the event and the people who were assassins (such as Gavrilo Princip). I still need this to work when viewing the event timeline of Gavrilo Princip.

Just to make this more complex, my “event” tab tiddler can be used on various types of tiddlers. I used the person tiddler only as an example. For example, this tab tiddler is also used on place tiddlers, to show a timeline for a given place.

Thank you for your complete and detailed explanation. Nice logic but complex.

Is this for demonstration? As I guess remove does not work in this way!

Yes.

I forgot the leading “+” sign…

+[remove[tag[event]contains:people{!!title}get[event]enlist-input[]contains:people{!!title}unique[]]
 :map:flat[all[tiddlers]tag[event]contains:event<currentTiddler>]]

This fails with

Filter error: Syntax error in filter expression

However, it worked with set widget and doing this:

+[remove<mylist>]

This is the correct syntax here! You cannot directly pass another filter operator as input to remove, but you can pass a title list (here you have created it using $set widget).

You may also use :except[tag[event]contains:people ...] see https://tiddlywiki.com/#Filter%20Expression

1 Like