diff --git a/Jenkinsfile b/Jenkinsfile index 757da78..f0463ff 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { VERSION = VersionNumber([ versionNumberString: '${BUILDS_ALL_TIME}', - versionPrefix: '1.3.', + versionPrefix: '1.4.', worstResultForIncrement: 'SUCCESS' ]) } diff --git a/README.md b/README.md index 1c792bc..ac0932e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @sapphirecode/utilities -version: 1.3.x +version: 1.4.x small utility functions to make much needed features easier to work with @@ -12,34 +12,49 @@ npm: yarn: -> yarn add @sapphirecode/utilities +> yarn add @sapphirecode/utilities ## Usage ```js const util = require('@sapphirecode/utilities'); +``` -// cut off decimal places to a specified point +cut off decimal places to a specified point + +```js util.truncate_decimal(12.345678, 2); // returns 12.34 +``` -// will return null instead of throwing on invalid json +will return null instead of throwing on invalid json + +```js util.try_parse_json('{{foo'); +``` -// copy an object to prevent modification of the original -const obj = {foo:'bar'}; +copy an object to prevent modification of the original + +```js +const obj = {foo: 'bar'}; const copy = util.copy_object(obj); copy.foo = 'baz'; console.log(obj.foo); // bar +``` -// run a regular expression and get a callback for every result -const data = "foobarfoo"; +run a regular expression and get a callback for every result + +```js +const data = 'foobarfoo'; const regex = /foo/g; -util.run_regex(regex, data, res => { +util.run_regex(regex, data, (res) => { console.log(res[0]); // will output 'foo' 2 times }); +``` -// check if a variable is null, undefined or NaN +check if a variable is null, undefined or NaN + +```js console.log(util.is_nil(parseInt('abc'))); // true console.log(util.is_nil('foo')); // false console.log(util.is_nil(42)); // false @@ -47,6 +62,54 @@ console.log(util.is_nil(null)); // true console.log(util.is_nil(undefined)); // true ``` +filter an array recursively + +```js +const to_filter = [ + {name: 'include_foo'}, + { + name: 'include_bar', + children: [{name: 'foo'}, {name: 'bar'}], + }, + { + name: 'baz', + children: [{name: 'include_foo'}, {name: 'bar'}], + }, + { + name: 'barbaz', + children: [{name: 'foo'}, {name: 'bar'}], + }, +]; +const filter = { + field: 'name', // the field the filter will apply on + filter: /^include_.*/iu, // regex filter +}; + +const result = util.recursive_filter( + to_filter, + [filter], // you can specify multiple filters, they will be connected using AND + 'children' // specify which field an objects children are stored in ('children' is default) +); + +console.log(JSON.stringify(result, null, 2)); +/* output: + +[ + { name: 'include_foo' }, + { + name: 'include_bar', + children: [ + { name: 'foo' }, + { name: 'bar' } + ] + }, + { + name: 'baz', + children: [ { name: 'include_foo' } ] + } +]*/ +``` + ## License MIT © Timo Hocker diff --git a/index.js b/index.js index 900861e..12b29e3 100644 --- a/index.js +++ b/index.js @@ -78,10 +78,46 @@ function is_nil (obj) { || (typeof obj === 'number' && isNaN (obj)); } +/** + * filter nested objects + * + * @param {Array} data + * @param {Array<{field: string, filter: RegExp}>} filters + * @returns {Array} filtered data + */ +function recursive_filter (data, filters, children_key = 'children') { + const filtered = []; + for (const e of data) { + let match = true; + for (const filter of filters) { + if (!filter.filter.test (e[filter.field])) { + match = false; + break; + } + } + if (match) { + filtered.push (e); + } + else { + if (typeof e[children_key] === 'undefined') + continue; + e[children_key] = recursive_filter ( + e[children_key], + filters, + children_key + ); + if (e[children_key].length > 0) + filtered.push (e); + } + } + return filtered; +} + module.exports = { truncate_decimal, try_parse_json, copy_object, run_regex, - is_nil + is_nil, + recursive_filter }; diff --git a/test/index.js b/test/index.js index 16dc628..ee4e56b 100644 --- a/test/index.js +++ b/test/index.js @@ -100,3 +100,50 @@ test ('check isnil with nan', (t) => { test ('check isnil with int', (t) => { t.is (util.is_nil (parseInt ('42')), false); }); + +test ('recursive filter', (t) => { + const raw = [ + { name: 'include_foo' }, + { + name: 'include_bar', + children: [ + { name: 'foo' }, + { name: 'bar' } + ] + }, + { + name: 'baz', + children: [ + { name: 'include_foo' }, + { name: 'bar' } + ] + }, + { + name: 'barbaz', + children: [ + { name: 'foo' }, + { name: 'bar' } + ] + } + ]; + const filtered = [ + { name: 'include_foo' }, + { + name: 'include_bar', + children: [ + { name: 'foo' }, + { name: 'bar' } + ] + }, + { + name: 'baz', + children: [ { name: 'include_foo' } ] + } + ]; + const filter = { + field: 'name', + filter: /^include_.*/ui + }; + const result = util.recursive_filter (raw, [ filter ]); + t.deepEqual (filtered, result); +});