Folks, Despite becoming quite familiar with the new function pragma there is a little gap I would like to fill. Is it in my understanding or does it point to a need for an improvement?
Consider the following
\function contains.string(string) [split<string>count[]compare:number:gt[1]]
{{{ [[This is a tiddler 2 that ]contains.string[tiddler]] }}}
The following custom filter splits the incoming title with the string and counts the parts. If there are more than one part then the string must exist in the incoming title. Noting that even if the title has a leading or trailing string this still works.
The custom operator will output the count if its greater than 1
Your will note that the custom operator is simply invoked with the input title. As far as I can tell there is no way to access this input title except to act on it with a filter. Our functions are by default what we call âselection modifiersâ, it is quite easy to ignore the incoming title by using a filter that is a âselection constructorâ such as starting with a variable [<varname>....
However
In the above function I would like to return the incoming title if the count is greater than 1 but I do not seem to have any way to reference this title, it seems to have being lost in the filter.
Common variables or fieldnames do not seem to work such as currentTiddler, title or {!!title} as these all refer to the current tiddler, not the current title in the filter.
There are other cases where I would like to have multiple filter runs each of which is applied to the incoming title, this is possible to some extent.
Also consider the following
\function test.function(string) [prefix[test]] [suffix[test]]
{{{ [[a title test]test.function[test]] }}}
{{{ [[test a title]test.function[test]] }}}
You will see each filter run, the prefix and suffix test, is handed the incoming title.
However if try to do anything else with the incoming title I loose access to it.
How can we otherwise access the incoming title, if we want to return that title or perform additional operations on it?
Might it be simpler to use \function contains.string(string) [search:title:literal,casesensitive<string>]?
Re: your broader question: this is the sort of situation in which I normally use filter or :filterâwhich is, I think, the normal Tiddlywiki way to return an input title unchanged. Notably, :filter works with functions, so you could do something like this:
\function contains.string(string) [split<string>count[]compare:number:gt[1]]
{{{ [[This is a tiddler 2 that ]] :filter[contains.string[tiddler]] }}}
Thanks @etardiff I suppose you have given me a âwork aroundâ, but I still think the question still stands to be answered.
I want a general solution to referencing the incoming title, the example given is primarily to illustrate the issue.
In someways the inability for the function to return the title, forces the way we write the calling filter, and depending on what that calling filter is trying to do it may add complexity, if not actually making it impossible to write.
Thanks, yes. I think what happens here is the abstraction of title into a literal string when the âtitleâ is in fact a logical title, although âtitleâ is listed in the âfield listâ, kind of conceals this approach. That is perhaps why I did not find it when looking.
You would be aware that some of the filter runs allow reference to both currentTiddler and ..currentTiddler, and I am thinking along those lines for inside functions.
So just for clarity, Using the search operator of @etardiff has the benefit that when found it does return the title passed into the filter. So the following is an effective work around for the original example;
\function has.string(string) [search:title:literal,casesensitive<string>]
{{{ [[This is a tiddler 2 that ]has.string[ 2 ]] }}}
In this case if the test is passed the input title is returned, so there is no need to reference it in the function, it just passes through if the string is found.
But to be clear
I am still asking how can we reference the âcurrent titleâ inside the function.
There may be cases where we canât use the method above
There will be cases where referencing the input title in a function, makes it simpler to write a function or have it self document.
Not a pretty example, but the :map[[]] prefix accesses the input title and could get you somewhereâŚ
\function contains.string(string:"this_is_an_unlikely_title_123?!") [split<string>count[]compare:number:gt[1]]
{{{ [[This is a tiddler 2 that ]] :map[contains.string[tiddler]then<currentTiddler>] }}}
Thankis to all above, I just found a solution. A very simple function that has access to the current title
\function function.title() [!is[blank]]
\function contains.string(string) [split<string>count[]compare:number:gt[1]then<function.title>]
{{{ [[This is a tiddler that contains 2]contains.string[2]] }}}
This is in fact the magic variable I am after;
So now if the string is found it returns the title in the function.
Conclusion
The specific example I gave is best solved with @etardiffâs use of
Thanks for this thread and your solution
For your function.title function you could use all[] as itâs a âpass-throughâ with its input.
\function function.title() [all[]]
Also I donât know if itâs related to your topic or more specifically to your example, but contains.string behaves weirdly when itâs passed more than one input string:
This example only returns the first matching input title. The culprit is the count operator, which counts results after split has been applied to every input, not separately for each input.
Just like your initial topic request, I would also like to have a currentInput or currentItem variable available inside a function definition. IMHO it would be consistent with currentTiddler and ..currentTiddler variables inside filter runs.
Thaks fred for your observations. I expected to see more variations of function.title and I suppose different arguments for which is more suitable. I think this supports the argument for a special variable to be made available. âcurrentItemâ is a good one.
I think you will find the weirdness is actualy how the tripple curly braces, or filtered transclusion operates, as well as when you reference the function as a variable '<<contains.string>>` but not when the function is used inside a filter= parameter.
this first value is a âfeatureâ, which I was not happy with when I first learned about it.
there are ways around this, like ending the function with +[join[ ]] or +[format:titlelist[]join[ ]] to set the custom operator value as a list. You may need to enlist this to use it.
I think you will find the count is done for each title, but without the above you only get to see the first result.
Try it and test what I am saying, I appreciate feedback.
@TW_Tones: Thank you for bringing up and finding a solution for a problem thatâs bothered me as well.
If we were going to propose a fix to core to ensure that thereâs always a variable available, would we be able to reuse currentTiddler? Or is there some reason that wouldnât be a good choice? If not, would there be any objection to @tw-FRedâs currentInput or currentItem?
Note that when a function is used in a filter run such as [tag[HelloThere]my.function[]], it is invoked exactly once and is passed all previous input titles. This is by design so that functions can also be written to be reductive if need be, which would not be possible if functions were invoked once for each input title. As such, there is no relevant value that can be assigned as currentTiddler since we are dealing with multiple input titles, which as pointed out above can already be accessed through all[].
In contrast, filter run prefixes such as :map, :reduce and :filter or the filter[] operator are invoked once per input title, which allows the variable currentTiddler to be set to the corresponding title each time.
As a special case, if the parameter is empty, the output is simply a copy of the input.
This makes all[] some kind of âidentity operatorâ, simply copying its input to its output, without filtering blank values.
Thus, {{{ [[]] [[A]] +[all[]count[]] }}} returns 2 where {{{ [[]] [[A]] +[!is[blank]count[]] }}} returns 1.
Scott, I think @saqimtiaz more than covers the use of currentTiddler there but to put it simply, it is a variable that already has a use in functions. The title I was after is they one passed into a function.
the title(s) are available to the function I just had no way to refer to it later in the function.
No objection to currentItem here. However the current solution is function.title which only works because its a custom function containing a period in its name.
I am now tempted to document this approach and not seek a special variable unless its trivial to implement.
@tw-FRed a new day here and is will respond to your doubt about my assertion;
however your used a different custom function than the one I published as the answer. This has become the subject of subsequent replies.
however the [all[]] behaves in a function, I still think [!is[blank]] is more to the point because it implies a simple test if a non-blank value exists in function.title. The introduction of [all[]... implies a whole new list.
The way I think of this is it is a single filter run, that commences with a list of titles, all the titles are then given one at a time to the custom filter operator.
I totaly agree with your point here, I would just âword itâ a little differently for a broader audience.
I am a little confused, could you please illustrate and explain again, what you think is weird a fresh? so we can run with that?
Sorry, must have being due to the late hours, I should have stated this as;
I think you will find the weirdness is actually how the triple curly braces, when used as an attribute, or filtered transclusion operates. Returning the first value only.
This illustrates the point I was making;
\function contains.string(string) [search:title:literal,caseinsensitive<string>]
\function function.title() [!is[blank]]
\function string.titles(string) [search:title:literal,caseinsensitive<string>count[]]
\function top.toc.contains(string) [tag[TableOfContents]contains.string<string>]
# {{{ [tag[TableOfContents]contains.string[t]] }}} returns list
# <$text text={{{ [tag[TableOfContents]contains.string[t]] }}}/> < Returns first only
# <<top.toc.contains t>> < Returns first only
# <$list filter=<<top.toc.contains t>> >
</$list> < Returns first only
# <$list filter="[top.toc.contains[t]]" >
</$list> < returns list