From c9e419d8e9775f1d330fc8dbc55d3c065e8bb591 Mon Sep 17 00:00:00 2001 From: Timo Hocker Date: Tue, 30 Jun 2020 10:00:36 +0200 Subject: [PATCH] filter groups --- Jenkinsfile | 2 +- README.md | 25 +++++++++++++++++++++++-- index.js | 47 ++++++++++++++++++++++++++++++++--------------- test/index.js | 21 +++++++++++++++++++++ 4 files changed, 77 insertions(+), 18 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e75c28a..4f122e4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { VERSION = VersionNumber([ versionNumberString: '${BUILDS_ALL_TIME}', - versionPrefix: '1.5.', + versionPrefix: '1.6.', worstResultForIncrement: 'SUCCESS' ]) } diff --git a/README.md b/README.md index 1fbbd50..3a0adc4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @sapphirecode/utilities -version: 1.5.x +version: 1.6.x small utility functions to make much needed features easier to work with @@ -118,13 +118,34 @@ const result = util.recursive_filter( [ { filter: /foo bar/iu, - fields: ['name', 'name_2'] // fields will be joined with a space in between + field: ['name', 'name_2'] // fields will be joined with a space in between // {name: 'foo', name_2: 'bar'} will become 'foo bar' } ] ); ``` +filter groups can be used to connect filters with OR + +```js +const result = util.recursive_filter( + [...], + [ + { + filter: /foo bar/iu, + field: 'name' + }, + { + or: [ + { filter: /foo/u, field: 'bar' }, + { filter: /bar/u, field: 'bar' } + ] + } + ] +); +``` + + ## License MIT © Timo Hocker diff --git a/index.js b/index.js index 49d8749..05d42f3 100644 --- a/index.js +++ b/index.js @@ -78,19 +78,45 @@ function is_nil (obj) { || (typeof obj === 'number' && isNaN (obj)); } -function to_search_string (element, field) { - return Array.isArray (field) - ? field.map ((f) => element[f]) +function check_filter (filter, e) { + const search_str = Array.isArray (filter.field) + ? filter.field.map ((f) => e[f]) .filter ((v) => typeof v !== 'undefined') .join (' ') - : element[field]; + : e[filter.field]; + + return filter.filter.test (search_str); } +function check_filters (filters, e, or = false) { + for (const filter of filters) { + let res = false; + if (Array.isArray (filter.or)) + res = check_filters (filter.or, e, true); + else + res = check_filter (filter, e); + + if (or && res) + return true; + if (!res && !or) + return false; + } + return !or; +} + +/** + * @typedef Filter + * @type {object} + * @property {string|string[]} field - fields to apply filter on + * @property {RegExp} filter - filter + * @property {Filter[]} or - create an OR group of filters + */ + /** * filter nested objects * * @param {Array} input - * @param {Array<{field: string, filter: RegExp}>} filters + * @param {Filter[]} filters * @returns {Array} filtered data */ function recursive_filter (input, filters, children_key = 'children') { @@ -99,16 +125,7 @@ function recursive_filter (input, filters, children_key = 'children') { for (let i = 0; i < data.length; i++) { const e = { ...data[i] }; data[i] = e; - let match = true; - for (const filter of filters) { - const search_str = to_search_string (e, filter.field); - - if (!filter.filter.test (search_str)) { - match = false; - break; - } - } - if (match) { + if (check_filters (filters, e)) { filtered.push (e); } else { diff --git a/test/index.js b/test/index.js index bfa7702..5cb28e4 100644 --- a/test/index.js +++ b/test/index.js @@ -204,3 +204,24 @@ test ('recursive filter undefined multifield', (t) => { ); t.deepEqual (res, []); }); + +test ('recursive filter with or group', (t) => { + const to_filter = [ + { name: 'foo' }, + { name: 'bar' }, + { name: 'baz' } + ]; + + const filter = [ + { + or: [ + { field: 'name', filter: /foo/u }, + { field: 'name', filter: /bar/u } + ] + } + ]; + + const res = util.recursive_filter (to_filter, filter); + + t.deepEqual (res, to_filter.slice (0, 2)); +});