From 14a28b77796461c9167898793ab9851e029e0354 Mon Sep 17 00:00:00 2001 From: "jeremy@jermolene.com" Date: Tue, 6 Oct 2020 19:20:03 +0100 Subject: [PATCH] Add "reduce" and "filter" operators --- core/modules/filters/filter.js | 31 +++++++++++ core/modules/filters/reduce.js | 54 +++++++++++++++++++ .../tiddlers/filters/examples/Brownies.tid | 9 ++++ .../tiddlers/filters/examples/Chick Peas.tid | 9 ++++ .../tiddlers/filters/examples/Milk.tid | 9 ++++ .../filters/examples/Rice Pudding.tid | 9 ++++ .../examples/filter Operator (Examples).tid | 23 ++++++++ .../examples/reduce Operator (Examples).tid | 26 +++++++-- editions/tw5.com/tiddlers/filters/filter.tid | 33 ++++++++++++ editions/tw5.com/tiddlers/filters/reduce.tid | 27 ++++++++++ .../tiddlers/filters/subfilter Operator.tid | 2 + 11 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 core/modules/filters/filter.js create mode 100644 core/modules/filters/reduce.js create mode 100644 editions/tw5.com/tiddlers/filters/examples/Brownies.tid create mode 100644 editions/tw5.com/tiddlers/filters/examples/Chick Peas.tid create mode 100644 editions/tw5.com/tiddlers/filters/examples/Milk.tid create mode 100644 editions/tw5.com/tiddlers/filters/examples/Rice Pudding.tid create mode 100644 editions/tw5.com/tiddlers/filters/examples/filter Operator (Examples).tid create mode 100644 editions/tw5.com/tiddlers/filters/filter.tid create mode 100644 editions/tw5.com/tiddlers/filters/reduce.tid diff --git a/core/modules/filters/filter.js b/core/modules/filters/filter.js new file mode 100644 index 000000000..2e549672b --- /dev/null +++ b/core/modules/filters/filter.js @@ -0,0 +1,31 @@ +/*\ +title: $:/core/modules/filters/filter.js +type: application/javascript +module-type: filteroperator + +Filter operator returning those input titles that pass a subfilter + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.filter = function(source,operator,options) { + var filterFn = options.wiki.compileFilter(operator.operand), + results = [], + target = operator.prefix !== "!"; + source(function(tiddler,title) { + var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title])); + if((list.length > 0) === target) { + results.push(title); + } + }); + return results; +}; + +})(); diff --git a/core/modules/filters/reduce.js b/core/modules/filters/reduce.js new file mode 100644 index 000000000..cc258af94 --- /dev/null +++ b/core/modules/filters/reduce.js @@ -0,0 +1,54 @@ +/*\ +title: $:/core/modules/filters/reduce.js +type: application/javascript +module-type: filteroperator + +Filter operator evaluats a subfilter for each item, making the running total available in the variable `accumulator`, and the current index available in the variable `index` + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.reduce = function(source,operator,options) { + // Accumulate the list + var results = []; + source(function(tiddler,title) { + results.push(title); + }); + // Run the filter over each item + var filterFn = options.wiki.compileFilter(operator.operand), + accumulator = operator.suffix || ""; + for(var index=0; index 0) { + accumulator = "" + list[0]; + } + } + return [accumulator]; +}; + +})(); diff --git a/editions/tw5.com/tiddlers/filters/examples/Brownies.tid b/editions/tw5.com/tiddlers/filters/examples/Brownies.tid new file mode 100644 index 000000000..c4b5a9af8 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/Brownies.tid @@ -0,0 +1,9 @@ +created: 20201004145650743 +modified: 20201006181234412 +price: 4.99 +quantity: 1 +tags: shopping +title: Brownies +type: text/vnd.tiddlywiki + +//This is a sample shopping list item for the [[Shopping List Example]]// diff --git a/editions/tw5.com/tiddlers/filters/examples/Chick Peas.tid b/editions/tw5.com/tiddlers/filters/examples/Chick Peas.tid new file mode 100644 index 000000000..fa222cc98 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/Chick Peas.tid @@ -0,0 +1,9 @@ +created: 20201004145612358 +modified: 20201006181232439 +price: 1.32 +quantity: 5 +tags: shopping +title: Chick Peas +type: text/vnd.tiddlywiki + +//This is a sample shopping list item for the [[Shopping List Example]]// diff --git a/editions/tw5.com/tiddlers/filters/examples/Milk.tid b/editions/tw5.com/tiddlers/filters/examples/Milk.tid new file mode 100644 index 000000000..9092e6d20 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/Milk.tid @@ -0,0 +1,9 @@ +created: 20201004145636906 +modified: 20201006181233518 +price: 0.46 +quantity: 12 +tags: shopping +title: Milk +type: text/vnd.tiddlywiki + +//This is a sample shopping list item for the [[Shopping List Example]]// diff --git a/editions/tw5.com/tiddlers/filters/examples/Rice Pudding.tid b/editions/tw5.com/tiddlers/filters/examples/Rice Pudding.tid new file mode 100644 index 000000000..d31f660e8 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/Rice Pudding.tid @@ -0,0 +1,9 @@ +created: 20201004145502135 +modified: 20201006181230956 +price: 2.66 +quantity: 4 +tags: shopping +title: Rice Pudding +type: text/vnd.tiddlywiki + +//This is a sample shopping list item for the [[Shopping List Example]]// diff --git a/editions/tw5.com/tiddlers/filters/examples/filter Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/filter Operator (Examples).tid new file mode 100644 index 000000000..c8afe9a0e --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/filter Operator (Examples).tid @@ -0,0 +1,23 @@ +created: 20201004141030951 +modified: 20201004143326056 +tags: [[Operator Examples]] [[filter Operator]] +title: filter Operator (Examples) +type: text/vnd.tiddlywiki + +\define larger-than-1k() [get[text]length[]compare:integer:gteq[1000]] +\define smaller-than-2k() [get[text]length[]compare:integer:lteq[2000]] +\define contains-missing-links() [links[]!is[shadow]is[missing]] +\define display-variable(name) +''<$text text=<<__name__>>/>'': <$text text={{{ [<__name__>getvariable[]] }}}/> +\end + +These examples use the following predefined variables: + +* <> +* <> +* <> + +<<.operator-example 1 "[tag[HelloThere]filter]">> +<<.operator-example 2 "[tag[HelloThere]filter]">> +<<.operator-example 3 "[tag[HelloThere]filterfilter]">> +<<.operator-example 4 "[tag[Features]filter]">> diff --git a/editions/tw5.com/tiddlers/filters/examples/reduce Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/reduce Operator (Examples).tid index 2c6e8b3d1..80f62582b 100644 --- a/editions/tw5.com/tiddlers/filters/examples/reduce Operator (Examples).tid +++ b/editions/tw5.com/tiddlers/filters/examples/reduce Operator (Examples).tid @@ -1,16 +1,34 @@ created: 20201004154413968 -modified: 20201004154413968 +modified: 20201006181831622 tags: [[Operator Examples]] [[reduce Operator]] title: reduce Operator (Examples) type: text/vnd.tiddlywiki -\define add-numbers() [get[text]length[]compare:integer:gteq[1000]] +\define add-price() [get[price]multiply{!!quantity}add] +\define num-items() [get[quantity]add] \define display-variable(name) ''<$text text=<<__name__>>/>'': <$text text={{{ [<__name__>getvariable[]] }}}/> \end These examples use the following predefined variables: -* <> +* <> +* <> -<<.operator-example 1 "[tag[HelloThere]filter]">> +They also use the following data tiddlers: + +
    +<$list filter="[tag[shopping]!has[draft.of]]"> +
  • +''<$link><$text text=<>/>'' quantity: <$text text={{!!quantity}}/>, price: <$text text={{!!price}}/> +
  • + +
+ +Number of items: + +<<.operator-example 1 "[tag[shopping]reduce]">> + +Total price: + +<<.operator-example 2 "[tag[shopping]reduce]">> diff --git a/editions/tw5.com/tiddlers/filters/filter.tid b/editions/tw5.com/tiddlers/filters/filter.tid new file mode 100644 index 000000000..faca51090 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/filter.tid @@ -0,0 +1,33 @@ +caption: filter +created: 20200929174420821 +modified: 20201006173606828 +op-input: a [[selection of titles|Title Selection]] passed as input to the filter +op-neg-input: a [[selection of titles|Title Selection]] passed as input to the filter +op-neg-output: those input titles that <<.em "do not">> pass the filter <<.place S>> +op-output: the [[selection of titles|Title Selection]] that pass the filter <<.place S>> +op-parameter: a [[filter expression|Filter Expression]] +op-parameter-name: S +op-purpose: apply a subfilter to each input title and return the titles that return a non-empty result from the subfilter +tags: [[Filter Operators]] [[Negatable Operators]] +title: filter Operator +type: text/vnd.tiddlywiki + +<<.from-version "5.1.23">> The <<.op filter>> operator runs a subfilter for each input title, and returns those input titles for which the subfilter returns a non-empty result (in other words the result is not an empty list). The results of the subfilter are thrown away. + +Simple filter operations can be concatenated together directly (eg `[tag[HelloThere]search[po]]`) but this doesn't work when the filtering operations require intermediate results to be computed. The <<.op filter>> operator can be used to filter on an intermediate result which is discarded. To take the same example but to also filter by those tiddlers whose text field is longer than 1000 characters: + +``` +<$vars myfilter="[get[text]length[]compare:integer:gteq[1000]]"> +<$list filter="[tag[HelloThere]search[po]filter]"> +
+<$link> +<$text text=<>/> + +
+ + +``` + +<<.tip "Compare with the similar [[subfilter|subfilter Operator]] operator which runs a subfilter and directly returns the results">> + +<<.operator-examples "filter">> diff --git a/editions/tw5.com/tiddlers/filters/reduce.tid b/editions/tw5.com/tiddlers/filters/reduce.tid new file mode 100644 index 000000000..fae41a1eb --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/reduce.tid @@ -0,0 +1,27 @@ +caption: reduce +created: 20201004154131193 +modified: 20201006174749170 +op-input: a [[selection of titles|Title Selection]] passed as input to the filter +op-output: the final result of running the subfilter <<.place S>> +op-parameter: a [[filter expression|Filter Expression]] +op-parameter-name: S +op-purpose: apply a subfilter to each input title in turn, accumulating a single value +op-suffix: Initial value for accumulator +op-suffix-name: V +tags: [[Filter Operators]] +title: reduce Operator +type: text/vnd.tiddlywiki + +<<.from-version "5.1.23">> The <<.op reduce>> operator runs a subfilter for each input title, passing the result of the previous subfilter run as a variable. The initial value of the accumulator can optionally be specified. It returns the result of the final subfilter run. + +The <<.op reduce>> operator is used to flatten a list of items down to a single item by repeatedly applying a formula. A typical use is to add up the values in a given field of a list of tiddlers. + +The following variables are available within the subfilter: + +* ''accumulator'' - the result of the previous subfilter run +* ''currentTiddler'' - the input title +* ''index'' - the numeric index of the current list item (with zero being the first item in the list) +* ''revIndex'' - the reverse numeric index of the current list item (with zero being the last item in the list) +* ''length'' - the total length of the input list + +<<.operator-examples "reduce">> diff --git a/editions/tw5.com/tiddlers/filters/subfilter Operator.tid b/editions/tw5.com/tiddlers/filters/subfilter Operator.tid index ec201fc85..a02c1f125 100644 --- a/editions/tw5.com/tiddlers/filters/subfilter Operator.tid +++ b/editions/tw5.com/tiddlers/filters/subfilter Operator.tid @@ -22,4 +22,6 @@ type: text/vnd.tiddlywiki ... ``` +<<.tip "Compare with the similar [[filter|filter Operator]] operator which runs a subfilter against each title, returning those titles that return a non-empty list (and discards the results of the subfilter)">> + <<.operator-examples "subfilter">>