diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2c3b89..54aa11f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,16 @@ Changelog ========= -* Add [`select` filter](https://mozilla.github.io/nunjucks/templating.html#select). +3.2.2 (unreleased) +------------------ + +* Add [`select`](https://mozilla.github.io/nunjucks/templating.html#select) and + [`reject`](https://mozilla.github.io/nunjucks/templating.html#reject) filters. + Merge of [#1278](https://github.com/mozilla/nunjucks/pull/1278) and + [#1279](https://github.com/mozilla/nunjucks/pull/1279); fixes + [#282](https://github.com/mozilla/nunjucks/issues/282). Thanks + [ogonkov](https://github.com/ogonkovv)! + 3.2.1 (Mar 17 2020) ------------------- diff --git a/docs/templating.md b/docs/templating.md index fcae50bc..5c213017 100644 --- a/docs/templating.md +++ b/docs/templating.md @@ -1389,6 +1389,32 @@ Select a random value from an array. A random value between 1-9 (inclusive). +### reject + +Filters a sequence of objects by applying a test to each object, and rejecting +the objects with the test succeeding. + +If no test is specified, each object will be evaluated as a boolean. + +**Input** + +```jinja +{% set numbers=[0, 1, 2, 3, 4, 5] %} + +{{ numbers | reject("odd") | join }} +{{ numbers | reject("even") | join }} +{{ numbers | reject("divisibleby", 3) | join }} +{{ numbers | reject() | join }} +``` + +**Output** + +```jinja +024 +135 +1245 +0 +``` ### rejectattr (only the single-argument form) diff --git a/nunjucks/src/filters.js b/nunjucks/src/filters.js index 5875dffc..b14c298b 100644 --- a/nunjucks/src/filters.js +++ b/nunjucks/src/filters.js @@ -266,22 +266,34 @@ function random(arr) { exports.random = random; +/** + * Construct select or reject filter + * + * @param {boolean} expectedTestResult + * @returns {function(array, string, *): array} + */ +function getSelectOrReject(expectedTestResult) { + function filter(arr, testName = 'truthy', secondArg) { + const context = this; + const test = context.env.getTest(testName); + + return lib.toArray(arr).filter(function examineTestResult(item) { + return test.call(context, item, secondArg) === expectedTestResult; + }); + } + + return filter; +} + +exports.reject = getSelectOrReject(false); + function rejectattr(arr, attr) { return arr.filter((item) => !item[attr]); } exports.rejectattr = rejectattr; -function select(arr, testName = 'truthy', secondArg) { - const context = this; - const test = context.env.getTest(testName); - - return arr.filter(function applyToTest(item) { - return test.call(context, item, secondArg); - }); -} - -exports.select = select; +exports.select = getSelectOrReject(true); function selectattr(arr, attr) { return arr.filter((item) => !!item[attr]); diff --git a/tests/filters.js b/tests/filters.js index c21900dc..26c610fc 100644 --- a/tests/filters.js +++ b/tests/filters.js @@ -490,6 +490,22 @@ finish(done); }); + it('reject', function(done) { + var context = { + numbers: [0, 1, 2, 3, 4, 5] + }; + + equal('{{ numbers | reject("odd") | join }}', context, '024'); + + equal('{{ numbers | reject("even") | join }}', context, '135'); + + equal('{{ numbers | reject("divisibleby", 3) | join }}', context, '1245'); + + equal('{{ numbers | reject() | join }}', context, '0'); + + finish(done); + }); + it('rejectattr', function(done) { var foods = [{ tasty: true