How to get the sum of field-values by filtering based on prefix of field-names

I was trying to solve this. For that I added a field called ct-sum into each day’s tiddler with field-value

{{{<currentTiddler>[fields[]prefix[ct-]] <currentTiddler>[fields[]prefix[cect-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]]}}}

And in the month’s tiddler, I put this code,

{{{[search:title<currentTiddler>get[ct-sum]sum[]]}}}

Is there any way to make it work - ? by using wikify ?

I believe so, its quite complex to explain, I may be off line, and I am definitely away from my desktop for the next day or so. Hopefully others can help.

Here is the basic approach.

Generate from a macro a list of all values, for all fields, for all tiddlers in a give filter. You need to make sure deduplication of titles (Ie the numbers) do not occur.

Wikify the result of the macro, then sum all the numbers.

  • Again make sure deduplication of titles (Ie the numbers) do not occur.

It would be good to be able to invoke filters from other filters, in this format

{{{[search:title<currentTiddler>invoke[ct-sum]sum[]]}}}

An invoked filter would need a ‘$title$’ for substituting the called title, in this case

[[$title$]][fields[]prefix[ct-]] :map[[$title$]get<currentTiddler>] +[sum[]]


/*\
title: $:/other/modules/filters/invoke.js
type: application/javascript
module-type: filteroperator

Filter operator that invokes other filters stored in a field
\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

/*
Export our filter function
*/
exports.invoke = function(source,operator,options) {
	var results = [];
	source(function(tiddler,title) {
		if(tiddler) {
			var value = tiddler.getFieldString(operator.operand);
			if(value) {
                value=value.replaceAll("$title$",title);
				results  = results.concat($tw.wiki.filterTiddlers(value));
			}
		}
	});
	return results;
};

})();
1 Like

We could maybe invoke macros:

\define makesum() [[$title$]][fields[]prefix[ct-]]  :map[[$title$]get<currentTiddler>] +[sum[]]


{{{[tag[tidsToSum]invokestring<makesum>sum[]]}}}


/*\
title: $:/other/modules/filters/invokestring.js
type: application/javascript
module-type: filteroperator

Filter operator that invokes other filters stored in a macro
\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

/*
Export our filter function
*/
exports.invokestring = function(source,operator,options) {
	var results = [];
	source(function(tiddler,title) {
		if(tiddler) {
			var value=operator.operand.replaceAll("$title$",title);
		    results  = results.concat($tw.wiki.filterTiddlers(value));

		}
	});
	return results;
};

})();
1 Like

This is looking like the foreshadowed function @buggyj or a new way to reference filters.

I think that’s what the subfilter operator is for: https://tiddlywiki.com/#subfilter%20Operator

1 Like

It is different, the subfilter operator does not have ‘title’ as a parameter it is run once. The new ‘invokestring’ filter takes ‘title’ as a parameter, and within it’s iterator substitutes the variable ‘$title$’ and runs this new filter. - it is run N times.

What you describe can be achieved using :map[subfilter<var>], like this:

\define makesum() [<currentTiddler>fields[]prefix[ct-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]]

{{{ [tag[tidsToSum]] :map[subfilter<makesum>] +[sum[]] }}}

Similar to your “invokestring” filter example, the makesum() macro defines a filter run sequence that computes the sum of all fields starting with ct- for a single tiddler. However, instead of replacing a $title$ placeholder, we use <currentTiddler> and <..currentTiddler> (for the “inner” :map filter run)

Then, in the invoking filter, we get the list of all tiddlers tagged with tidsToSum and use :map[subfilter<makesum>] filter run prefix to apply the makesum filter to get a list of ct-* field totals for each tagged tiddler, and then add those results together using the last +[sum[]] filter run.

2 Likes

Great stuff!! So it can be done.

So in essence I was missing that map[subfilter<makesum>] sets the variable <<currentTiddler>> before calling <<makesum>>.

I would be good to add an example of this usage to the docs

1 Like

@buggyj Thanks for taking time to help me out.

@EricShulman Thanks for the wikitext based solution you provided. I will have to test it out before commenting more. It works, but I have to make some modifications to suit my tiddler structure.

A small doubt how to modify the code to include fields with both ct- and cect- prefix. When I tested my code wasn’t working, must be due to some errors.

\define ct-sum() [<currentTiddler>[fields[]prefix[ct-]] [<currentTiddler>[fields[]prefix[cect-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]]

Plain CT and CECT ={{{ [tag[Journal]month<currentTiddler>] :map[subfilter<ct-sum>] +[sum[]] }}}

You have an extra “[” preceding each “fields[]” operator in the ct-sum() filter syntax.

Change this:
\define ct-sum() [<currentTiddler>[fields[]prefix[ct-]] [<currentTiddler>[fields[]prefix[cect-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]]
to this:
\define ct-sum() [<currentTiddler>fields[]prefix[ct-]] [<currentTiddler>fields[]prefix[cect-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]]

Thanks @EricShulman. How did I miss that. Was a busy day today. Wasn’t able to concentrate fully.

@EricShulman I want to multiply the numerical value derived from this filter {{{ [tag[Journal]month<currentTiddler>] :map[subfilter<ct-sum>] +[sum[]] }}} with another number (for example say 100), how to do that ?

Use the multiply[100] filter operator like this:

{{{ [tag[Journal]month<currentTiddler>] :map[subfilter<ct-sum>] +[sum[]multiply[100]] }}}
1 Like
{{{ [tag[Journal]month<currentTiddler>] :map[subfilter<ct-sum>] +[sum[]multiply[400]] }}}

{{{ [tag[Journal]month<currentTiddler>] :map[subfilter<usg-sum>] +[sum[]multiply[400]] }}}

{{{ [tag[Journal]month<currentTiddler>] :map[subfilter<mri-sum>] +[sum[]multiply[600]] }}}

@EricShulman I need to get the sum of these three filters. Is it possible in a single filter step or will I have to do it in seperate steps ?

You could define a combined subfilter:

\define all-sum()
[<currentTiddler>[fields[]prefix[ct-]]
[<currentTiddler>[fields[]prefix[usg-]]
[<currentTiddler>[fields[]prefix[mri-]]
:map[<..currentTiddler>get<currentTiddler>]
+[sum[]]
\end

This would allow you to then write:

{{{ [tag[Journal]month<currentTiddler>] :map[subfilter<all-sum>] +[sum[]multiply[400]] }}}

However, I assume that you have defined three different subfilters (for ct-, usg-, and mri- field prefixes) because you want to also compute individual sums for each of those field prefixes.

If this is the case, then without adding the all-sum subfilter definition, you could write:

$let ct-total={{{ [tag[Journal]month<currentTiddler>] :map[subfilter<ct-sum>] +[sum[]multiply[400]] }}}
    usg-total={{{ [tag[Journal]month<currentTiddler>] :map[subfilter<usg-sum>] +[sum[]multiply[400]] }}}
    mri-total={{{ [tag[Journal]month<currentTiddler>] :map[subfilter<mri-sum>] +[sum[]multiply[400]] }}}
    all-total={{{ [<ct-total>add<usg-total>add<mri-total>] }}}>
1 Like

It works @EricShulman . Thank you.

As discussed in this thread, I want to get the sum of all fields with a prefix ct- and cect- in a particular tiddler . I want to store this sum in a field called ct-sum of the same tiddler so that I can use this value in a dynamic table. I added this code

{{{<currentTiddler>[fields[]prefix[ct-]] <currentTiddler>[fields[]prefix[cect-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]]}}} 

as field value of ct-sum field.

When I transclude the field ct-sum in the same tiddler like {{!!ct-sum}}, I am getting the sum value, but when I am using the field ct-sum in the shiraz dynamic table, the value is not seen (instead zero is seen)

Why is it happening like that

You have some bracketing errors, to begin with. Try this:

{{{ [<currentTiddler>fields[]prefix[ct-]] [<currentTiddler>fields[]prefix[cect-]] :map[<..currentTiddler>get<currentTiddler>] +[sum[]] }}}

(As a side note, I prefer to use spaces between the triple braces of the transclusion and the filter run square brackets—even though the spaces aren’t strictly necessary—because it makes it easier to spot missing or mismatched brackets.)

If that doesn’t solve the issue…

Without looking at your dynamic table code, I’d assume that there’s a list widget in play somewhere, and either it’s changing the value of <<currentTiddler>> to a different tiddler that doesn’t have the " ct-sum" field, or it’s setting and relying upon a different variable that your filter isn’t using (and thus, again, you’re trying to transclude the field from a tiddler that doesn’t have it).

https://be-rad-scribe.tiddlyhost.com/#March2023

This is an example tiddler. I was not able to get it working