How To: Fun with functions beyond TiddlyWiki 5.3.0 release

Folks,

As demonstrated in https://tiddlywiki.com/prerelease the new function, which I remember it “begins with F for filter”, we will be able to craft our own filter operators.

Personally I feel this will be a great tool for simplifying our code, especially filters.

  • Here is a simple example, where I define two filters. One is a custom `.is’ operator that tests if the tiddler has a matching value in the object-type field.
  • Similarly another custom filter operator tests if the done-date field has any value, so we can also ask !.done[]
  • The new filter tests if the tiddler has the same date as today
  • The same can also be applied against the current tiddler.
\function .is(object-type) [object-type<object-type>] 
\function .done() [has[done-date]]
\function .new() [get[created]format:date[YYYY0MM0DD]match<now YYYY0MM0DD>then[new]]

<$list filter="[.is[todo]!.done[]]">
   <$link/> = {{!!object-type}}<br>
</$list>

{{{ [all[current].is[todo]then[todoitem]else[not todo]] }}}

{{{ [all[current].new[]] }}}

Some clear advantages with filter operators like this;

  • You can hide the complexity in functions and make your filters easier to read
  • You can create generic functions/filters you can use in different cases eg; any tiddler todo or otherwise that has[done-date] will be considered done
    • Thus you could mark a “chapter tiddler” done as well.
    • Or you could change this function definition to [tag[done]] in a different wiki, yet make use of the same list widget code.
  • you can modify the operators later to add functionality without modifying every time the filter operator is used.
    • Eg; refine .done to test done-date is today or earlier
2 Likes

I think the custom .is you propose is visually way to close to the core is-operator. So in the end it will be harder to understand instead of simpler.

So I’m not happy with tha name.
Just my personal opinion.

Interesting. To me the values is its closeness. It reads the same. I would prefer if it were “is” with extencible values.

  • objection registered but it is for illustration you could use .tidder.is or other, any ideas?

In my own notes I always call functions Filter Functions instead of just calling Functions!

2 Likes

Maybe its because I’ve been a coder for too long. I prefer my code to be self-documenting. Reading the code itself should tell me exactly what it is doing.

And its the same with filters. Its commendable to want to simplify your filters, but the names you use for your new filter functions are a little too vague. Reading the filter one year later, I’m concerned I wouldn’t immediately figure out what it does. Thus I would:

  • Rename .is to .typeis
  • Remove .done since has[done-date] is much clearer
  • Rename .new to something with a longer name…

That said, I would almost immediately add:

\function .current() [all[current]]
\function .all() [all[shadows+tiddlers]]

3 Likes

This is one thing I achieve here using .done because it could be [has[done-date]] or [tag[done]] or in another wiki is may be [has[done]], as these will be global functions defined in a single place. That is, it sets a standard.

  • In this case I am setting a standard, perhaps with a dozen of more filters I will use consistently across all my wikis, for for me it becomes much simpler.
  • I can also introduce these with tweaks in the function definition to suit other wikis that have different ways to determine the same condition, again it is self documenting.

Of importance to the examples I gave we can use these operators both to test the current tiddler, and list matching tiddlers

  • [all[current]!.done[]]
  • [all[tiddlers]!.done[]]

Here I extend this to a date test for start-date and end-date;

\function .startable() [has:field[start-date]!has[start-date]then[true]]
\function .started() [get[start-date]compare:date:lteg<now YYYY0MM0DD0hh0mm0ss0XXX>]
\function .starting() [get[start-date]compare:date:gt<now YYYY0MM0DD0hh0mm0ss0XXX>]
\function .ended() [get[end-date]compare:date:lteq<now YYYY0MM0DD0hh0mm0ss0XXX>]
\function .ending() [get[end-date]compare:date:gt<now YYYY0MM0DD0hh0mm0ss0XXX>]
  • They only return a single value if “true” and then if relevant, the value you require.

I encourage everyone to consider the new opportunities and share them here. Whatever your personal preferences.

1 Like

Here is a set of the current ways to determine which half year or quarter it is or for a given date;

\define now-yearhalf() {{{ [<now MM>compare:number:lt[7]then[1]else[2]] }}}
\define yearhalf(date) {{{ [[$date$]format:date[0MM]compare:number:lt[7]then[1]else[2]] }}}
\define quarter(date) {{{ [[$date$]format:date[0MM]divide[3]untrunc[]] }}}

* `H<$macrocall $name=now-yearhalf/>` H<$macrocall $name=now-yearhalf/>
* `<$macrocall $name=yearhalf date="20220715"/>` <$macrocall $name=yearhalf date="20220715"/>
* `<<$macrocall $name=quarter date={{!!created}}/>` <$macrocall $name=quarter date={{!!created}}/>
* `<$macrocall $name=quarter date="20231217"/>` <$macrocall $name=quarter date="20231217"/>
  • Although you could also remove the {{{ }}} in each macro and use the subfilter operator [subfilter<now-yearhalf>]
  • Note how the result always appears as a link to a tiddler.
    • The standard way is to wrap this in the verbose <$text widget.
  • In the above example you could use a template the “text” tiddler containing as follows;
    • <$text text=<<currentTiddler>>/>
  • I do use the text tiddler as a template in the following function examples

Here are examples of the new approach on 5.3.0

\function now-yearhalf() [<now MM>compare:number:lt[7]then[1]else[2]]
\function yearhalf(date) [<date>format:date[MM]compare:number:lt[7]then[1]else[2]]
\function quarter(date) [<date>format:date[0MM]divide[3]untrunc[]]
\function .now-yearhalf() [<now MM>compare:number:lt[7]then[1]else[2]]
\function .yearhalf(date) [<date>format:date[MM]compare:number:lt[7]then[1]else[2]]
\function .quarter(date) [<date>format:date[0MM]divide[3]untrunc[]]

h{{{ [function[now-yearhalf]]||text}}}
h{{{ [function[yearhalf],{!!created}]||text}}}
q{{{ [function[quarter],{!!created}]||text}}}
<hr>
h{{{ [.now-yearhalf[]]||text}}}
h{{{ [.yearhalf{!!created}]||text}}}
q{{{ [.quarter{!!created}]||text}}}
  • Using the text template is a workable solution when displaying the results of a filtered transclusion in the new and original approaches.

Question

Since we are now and will under 5.3.0 most likely use filtered transclusions a lot more could we not find a new way to stop their results becoming a link?

  • Or to put it another way a simple way to indicate the contents should be plain text, rather than use the text widget?
    • I thought I raised a github issue for this but can’t find it. must have being inside the 5.3.0 preworks and lost.
1 Like

Hi @TW_Tones,

You can do this:

Create a new tiddler named “T” with text:

{{!!title}}

Then any filtered transclusion can be displayed as simple text like this: {{{[[My filter]]||T}}}

Fred

1 Like

Yes, that’s almost identical to mine, T contains the text widget, but there are many cases without use of a transclusion we want links stripped returning only the text.

  • To keep the text template out of search I have used Unicode such as 𝕥𝕖𝕩𝕥 for its title.

New way to use functions

Wow, see what is possible as a result of another way to use functions In need for a cascade:all[] Filter Run Prefix - #16 by TW_Tones

Thanks @btheado This may solve issues I have raised elsewhere, when trying to document and reuse filters, or construct filters.

When used with join it actually is an approach to concatenation and avoids substitution or backtick attributes.

  • We may need to document this.

What a trick :clap: :nerd_face:

\function construct.filter() "[" "length[]match[3]" "prefix[ca]" "]" +[join[]]

<<construct.filter>>

Returns: [length[]match[3]prefix[ca]]

  • This may even permit a rewrite of the solution to this topic?
  • Note you can even include evaluated filters for variables, fields etc…

We can say;

  • Did you know you can use literals in functions?
  • Functions to prepare strings, tooltips and more.
  • Functions to generate plain text stings
  • Functions to construct filters

Thanks, @TW_Tones. I did mention this trick one other time when I discovered it. I went into a bit more detail there: How to Delimit more than one filter in a string or field? - #14 by btheado

1 Like

Thanks a lot again @btheado, this goes even further. I see my prior involvement some time ago. I think I did not realise how big a scope of possibilities existed but with 5.3x and especially functions, this opens things up even further.

  • Your use of let is also informative because we can construct the field/var names along side the field/var values, giving it a semantically readable form.
  • Combine this set multiple variables, set multiple fields action, the parameters $params parameter and more I think a lot of “meta language” code is now possible.

In some ways we get to construct & defer the evaluation of a filter, so we can display it before using it;

\define start() 2
\define end() 12
\define step() 2
\function range.func() "[range<start>,<end>,<step>]"

<<range.func>>

<$list filter=<<range.func>> >

</$list>
1 Like