diff --git a/filter.js b/filter.js new file mode 100644 index 0000000..29851f4 --- /dev/null +++ b/filter.js @@ -0,0 +1,114 @@ +'use strict'; + +function get_element_field (element, carried_data, field) { + if (typeof carried_data[field] !== 'undefined') + return carried_data[field]; + return element[field]; +} + +function check_filter (filter, e, carried_data) { + if (typeof filter.function !== 'undefined') + return filter.function ({ ...e, ...carried_data }); + + const search_str = Array.isArray (filter.field) + ? filter.field.map ((f) => get_element_field (e, carried_data, f)) + .filter ((v) => typeof v !== 'undefined') + .join (' ') + : get_element_field (e, carried_data, filter.field); + + return filter.filter.test (search_str); +} + +function check_filters (filters, e, carried_data, or = false) { + for (const filter of filters) { + let res = false; + if (Array.isArray (filter.or)) + res = check_filters (filter.or, e, carried_data, true); + else + res = check_filter (filter, e, carried_data); + + 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 + */ + +/** + * @typedef FilterFunction + * @type {object} + * @property {function} function - function to test element for match + */ + +/** + * @typedef FilterOrGroup + * @type {object} + * @property {FilterType[]} or - create an OR group of filters + */ + +/** + * @typedef FilterType + * @type {Filter|FilterFunction|FilterOrGroup} + */ + +/** + * filter nested objects + * + * @param {Array} input object to filter + * @param {FilterType[]} filters filters + * @param {string[]} carry carry data to children to match + * @param {object} carried_data internal: carried data + * @returns {Array} filtered data + */ +function recursive_filter ( + input, + filters, + children_key = 'children', + carry = [], + carried_data = {} +) { + const data = [ ...input ]; + const filtered = []; + + for (const c of carry) { + if (typeof carried_data[c] !== 'string') + carried_data[c] = ''; + } + + for (let i = 0; i < data.length; i++) { + const e = { ...data[i] }; + data[i] = e; + const sub_carry = { ...carried_data }; + for (const c of carry) { + if (typeof e[c] !== 'undefined') + sub_carry[c] += `${sub_carry[c].length > 0 ? ' ' : ''}${e[c]}`; + } + if (check_filters (filters, e, sub_carry)) { + filtered.push (e); + } + else { + if (typeof e[children_key] === 'undefined') + continue; + e[children_key] = recursive_filter ( + e[children_key], + filters, + children_key, + carry, + sub_carry + ); + if (e[children_key].length > 0) + filtered.push (e); + } + } + return filtered; +} + +module.exports = recursive_filter; diff --git a/index.js b/index.js index 1958192..999e9cd 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,8 @@ 'use strict'; +const recursive_filter = require ('./filter'); + /** * truncates a floating point number * @@ -78,117 +80,6 @@ function is_nil (obj) { || (typeof obj === 'number' && isNaN (obj)); } -function get_element_field (element, carried_data, field) { - if (typeof carried_data[field] !== 'undefined') - return carried_data[field]; - return element[field]; -} - -function check_filter (filter, e, carried_data) { - if (typeof filter.function !== 'undefined') - return filter.function ({ ...e, ...carried_data }); - - const search_str = Array.isArray (filter.field) - ? filter.field.map ((f) => get_element_field (e, carried_data, f)) - .filter ((v) => typeof v !== 'undefined') - .join (' ') - : get_element_field (e, carried_data, filter.field); - - return filter.filter.test (search_str); -} - -function check_filters (filters, e, carried_data, or = false) { - for (const filter of filters) { - let res = false; - if (Array.isArray (filter.or)) - res = check_filters (filter.or, e, carried_data, true); - else - res = check_filter (filter, e, carried_data); - - 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 - */ - -/** - * @typedef FilterFunction - * @type {object} - * @property {function} function - function to test element for match - */ - -/** - * @typedef FilterOrGroup - * @type {object} - * @property {FilterType[]} or - create an OR group of filters - */ - -/** - * @typedef FilterType - * @type {Filter|FilterFunction|FilterOrGroup} - */ - -/** - * filter nested objects - * - * @param {Array} input object to filter - * @param {FilterType[]} filters filters - * @param {string[]} carry carry data to children to match - * @param {object} carried_data internal: carried data - * @returns {Array} filtered data - */ -function recursive_filter ( - input, - filters, - children_key = 'children', - carry = [], - carried_data = {} -) { - const data = [ ...input ]; - const filtered = []; - - for (const c of carry) { - if (typeof carried_data[c] !== 'string') - carried_data[c] = ''; - } - - for (let i = 0; i < data.length; i++) { - const e = { ...data[i] }; - data[i] = e; - const sub_carry = { ...carried_data }; - for (const c of carry) { - if (typeof e[c] !== 'undefined') - sub_carry[c] += `${sub_carry[c].length > 0 ? ' ' : ''}${e[c]}`; - } - if (check_filters (filters, e, sub_carry)) { - filtered.push (e); - } - else { - if (typeof e[children_key] === 'undefined') - continue; - e[children_key] = recursive_filter ( - e[children_key], - filters, - children_key, - carry, - sub_carry - ); - if (e[children_key].length > 0) - filtered.push (e); - } - } - return filtered; -} - module.exports = { truncate_decimal, try_parse_json, diff --git a/package.json b/package.json index d150843..eae26a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sapphirecode/utilities", - "version": "1.8.7", + "version": "1.8.8", "main": "index.js", "author": { "name": "Timo Hocker", @@ -36,8 +36,8 @@ }, "files": [ "LICENSE", - "index.js", - "index.d.ts" + "*.js", + "*.d.ts" ], "engines": { "node": ">=10"