Checking the presence of key in a dictionary tiddler, to conditionally add a flag to an element of a list

Hello all! This one is most likely a silly syntax issue, but I might also be confusing TW with the structure I’m using. In any case, I’m completely puzzled and would love any help.

I have dictionary tiddlers (simple key:value pairs inside), listing spell names and their corresponding levels:

spell1 name:level
spell2 name:level
...
spell3 name:level

(I call those tiddlers “spellbooks”)

In another regular tiddler holding information on a character, a spellbook field indicates what dictionary tiddler holds the list of spells that character knows, and how high level he/she does.

Inside the character tiddler, I’m listing the contents of the field-specified spellbook of this character like so:

<$let spellbook={{!!spellbook}}>
''Spellbook'': <$link to=<<spellbook>>/>

<$macrocall $name="gurpsspelltable" spells="[<spellbook>indexes[]]"/> 
</$let>

… where the gurpsspelltable macro is defined as:

\define gurpsspelltable(spells)
<table class="max">
<tr>
<th align="left">Spell</th>
<th align="center">Level</th>
<th align="center">College</th>
<th align="center">Class</th>
<th align="center">Base cost</th>
<th align="center">Additional cost</th>
<th align="center">Time to cast</th>
<th align="center">Duration</th>
<th align="center">Prerequisites</th>
</tr>
<$list filter="$spells$" template=GURPSSpellTableRowTemplate  variable="spell"/>
</table>
\end

… then the template GURPSSpellTableRowTemplate generates the row for each of the keys in the spellbook dictionary tiddler, i.e spell1 name, spell2, ..., spelln:

<$let level={{{ [<spellbook>getindex<spell>] ~0 }}}> <!-- displays "0" if the <<spell>> index does not exist or is blank -->
<tr><td><$link to=<<spell>>/></td>
<td><<level>></td>
<td>{{{ [<spell>get[college]] }}}</td>
<td><$let class={{{ [<spell>get[class]] }}}><<class>></$let></td>
<td><$let basecost={{{ [<spell>get[base_cost]] }}}><$macrocall $name="cost-fp" value="<<basecost>>"/></$let></td>
<td><$let addcost={{{ [<spell>get[additional_cost]] }}}><$list filter="[<addcost>split[ ]]" variable="seg"><% if [<seg>regexp[\d+]] %><$macrocall $name=cost-fp value=<<seg>> /> <% else %><<seg>> <% endif %></$list></$let></td>
<td><$let ttc={{{ [<spell>get[casting_time]] }}}><$macrocall $name="time" value="<<ttc>>"/></$let></td>
<td><$let dur={{{ [<spell>get[duration]] }}}><$macrocall $name="duration" value="<<dur>>"/></$let></td>
<td><$let prereq={{{ [<spell>get[prerequisites]] }}}><$list filter="[<prereq>split[, ]]" variable="segm" join=", "><$link to=<<segm>>/><% if [[[<spellbook>indexes[]]!contains[<segm>]] %> 
 (!)<% endif %></$list></$let></td></tr>
</$let>

The issue resides in the last row, which lists other spell names that are prerequisites for a spell to be known; I want to display “(!)” after a any spell name of the prerequisite list for a spell, that is missing from the spellbook. (Note: the prerequisites are listed in plain text in the prerequisites field of any spell, in the form of spell1 name, spell2 name, ..., spelln name)

I’m trying to do that through the following segment:

<$let prereq={{{ [<spell>get[prerequisites]] }}}><$list filter="[<prereq>split[, ]]" variable="segm" join=", "><$link to=<<segm>>/><% if ![[<spellbook>indexes[]]contains[<segm>]] %> 
 (!)<% endif %></$list></$let>

But I’m getting “(!)” after every spell in the prerequisites even when they are listed in the spellbook. In the image below featuring the rendered character tiddler, the (!) that should not be there are circled in red:

Evidently,

<% if ![[[<spellbook>indexes[]]contains[<segm>]] %> 
 (!)<% endif %>

… doesn’t work like I’m expecting it to.

Can any TW tenor point me in the right direction? Is it indeed a syntax mistake, or the segmenting of the listing process through macros and stuff that gives me this unwanted behaviour?

Thanks in advance for all help and suggestions provided :slight_smile:

  • You’ve got too many square brackets
  • The contains filter operator expects a tiddler title as input, and checks to see if a field of that tiddler (default field=“list”) contains the specified value. It doesn’t work with indexes.

Instead, try this filter syntax:

<% if [<spellbook>!has:index<segm>] %>  (!)<% endif %>

This is a more direct way to check to see if the <spellbook> tiddler has a definition for the index specified by the value of <segm>

enjoy,
-e

1 Like

Oh that’s neat :slight_smile: Thanks @EricShulman ! Is there a way to also elegantly exclude empty prerequisites fields from displaying the (!)? This filter understandably counts them as not having <segm> since they’re empty.

Placing another filter around the one you suggested to first check if the field is empty works well;

<% if [<spell>has[prerequisites]] %><% if [<spellbook>!has:index<segm>] %>  (!)<% endif %><% endif %>

… but maybe there’s a simpler syntax?

You can make the syntax a little simpler by combining both conditions in a single filter run, like this:

<% if [<spell>has[prerequisites]then<spellbook>!has:index<segm>] %>  (!)<% endif %>

-e

1 Like

I’m now trying to count the number of spells (keys) in a spellbook that have a given value in their college field. I’m pretty sure I can do that with the count operator, but I’m unsure how to do it with my structure.

<$list filter="<spellbook>indexes[]]" variable="spell"><$count filter=[<spell>get[college]each[]]/></$list>

Doesn’t yield anything but a 1, so evidently, I haven’t understood how it works.

The end goal is to display a line listing all the different colleges and their count for the spells in the <spellbook>indexes[] list, something like:

Air spell: 2, Fire spell: 1

… for example, is there are indeed 2 spells with the college field “Air spell” and 1 spell with the college field “Fire spell” among the spells listed as keys in the spellobook dictionary tiddler.

Any idea?

Try this out (untested):

<$list filter="[<spellbook>indexes[]each[college]get[college]]" variable="college" join=", ">
	<<college>>: <$count filter="[<spellbook>indexes[]college<college>]" />
</$list>

Breaking it down:

  • First, we want a list of all the unique values of the college field among the spells indexed in <<spellbook>>.
    • [<spellbook>indexes[]... gets us the list of indexes = in this case, the list of spells.
    • each[college] narrows it down to only those spells with a college value not yet represented by another spell in the list. It will return the title of each spell with a unique field value, not that field value itself. To get that, we need…
    • get[college] — the final step of this list.
  • Now that we have a list of unique field values, we want to count the number of spells that have each field value.
    • Starting again from [<spellbook>indexes[]...
    • college<college> (or the longer form field:college<college>) returns only the spells whose college field matches the current value of <<college>>, i.e. the current iteration of the parent $list.

Lastly — the very first reason your original filter would have failed is because you’re missing the initial [ at the beginning of the filter run. It should be filter="[<spellbook>... not filter="<spellbook>...

Missing or misplaced/mismatched brackets are responsible for probably 95% of my filter woes, so it’s always a good “easy mistake” to check for first. Those Pesky Brackets has a nice overview of when to use each type. As a general rule:

  • One set of square brackets wrapping each filter run. If using a filter run prefix like + or ~ or :map, this comes before [.
  • A single filter (i.e. everything inside filter="...") may include one or more filter runs. I like to use a space or a linebreak between every filter run to make them easier to read, but this isn’t strictly necessary.
  • Inside a filter run, use one set of any brackets used to indicate a filter operator’s parameter: tag[literal], tag<variable>, or tag{!!transclusion}.
  • If you ever have more than two brackets of any type in a row, double-check your work; something has probably gone wrong.
2 Likes

Your solution seems to work perfectly, thanks!

Fantastic general rule as well :slight_smile: