Filters an array of objects with multiple match-criteria.
/**
* Filters an array of objects by custom predicates.
*
* @param {Array} array: the array to filter
* @param {Object} filters: an object with the filter criteria
* @return {Array}
*/
function filterArray(array, filters) {
const filterKeys = Object.keys(filters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores non-function predicates
if (typeof filters[key] !== 'function') return true;
return filters[key](item[key]);
});
});
}
describe('Testing filterArray()', () => {
it('should filter an array of objects by custom predicates', () => {
const products = [
{ name: 'A', color: 'Blue', size: 50, locations: ['USA', 'Europe'], details: { length: 20, width: 70 } },
{ name: 'B', color: 'Blue', size: 60, locations: [], details: { length: 20, width: 70 } },
{ name: 'C', color: 'Black', size: 70, locations: ['Japan'], details: { length: 20, width: 71 } },
{ name: 'D', color: 'Green', size: 50, locations: ['USA'], details: { length: 20, width: 71 } },
];
const filters = {
size: size => size === 50 || size === 70,
color: color => ['blue', 'black'].includes(color.toLowerCase()),
locations: locations => locations.find(x => ['JAPAN', 'USA'].includes(x.toUpperCase())),
details: details => details.length < 30 && details.width >= 70,
};
const filtered = filterArray(products, filters);
const expected = [
{ name: 'A', color: 'Blue', size: 50, locations: ['USA', 'Europe'], details: { length: 20, width: 70 } },
{ name: 'C', color: 'Black', size: 70, locations: ['Japan'], details: { length: 20, width: 71 } },
];
expect(filtered).toStrictEqual(expected);
});
});
// ignores case-sensitive
const getValue = value => (typeof value === 'string' ? value.toUpperCase() : value);
/**
* Filters an array of objects (one level-depth) with multiple criteria.
*
* @param {Array} array: the array to filter
* @param {Object} filters: an object with the filter criteria
* @return {Array}
*/
function filterPlainArray(array, filters) {
const filterKeys = Object.keys(filters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!filters[key].length) return true;
return filters[key].find(filter => getValue(filter) === getValue(item[key]));
});
});
}
describe('Testing filterPlainArray()', () => {
it('should filter an array of objects with one level-depth', () => {
const products = [
{ name: 'A', color: 'Blue', size: 50 },
{ name: 'B', color: 'Blue', size: 60 },
{ name: 'C', color: 'Black', size: 70 },
{ name: 'D', color: 'Green', size: 50 },
];
const filters = {
color: ['BLUE', 'black'],
size: [70, 50],
};
const filtered = filterPlainArray(products, filters);
const expected = [
{ name: 'A', color: 'Blue', size: 50 },
{ name: 'C', color: 'Black', size: 70 },
];
expect(filtered).toStrictEqual(expected);
});
});