Skip to content

Instantly share code, notes, and snippets.

@webdesignberlin
Forked from jherax/filterArray.js
Created June 2, 2021 11:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save webdesignberlin/4c054a9d3b3a42ad2a7a0425443cc6c3 to your computer and use it in GitHub Desktop.
Save webdesignberlin/4c054a9d3b3a42ad2a7a0425443cc6c3 to your computer and use it in GitHub Desktop.
Filters an array of objects with multiple match-criteria.
/**
* Filters an array of objects using 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]);
});
});
}
/**
* The method `filterArray()` has the following signature:
*
* `function filterArray<TInput = any>(array: TInput[], filters: IFilters) => TInput[]`
*
* Where the function receives an array as the first argument, and a plain object
* describing the fields to filter as the last argument.
* The function returns an array of the same type as the input array.
*
* The signature of the filters arguments is the following:
*
* `interface IFilters {
* [key: string]: (value: any) => boolean;
* }`
*
* Where the `filters` argument is an object that contains a `key: string`
* and its value is a function with the value of the property to evaluate.
* As the function predicate is evaluated using the `Array.prototype.every()` method,
* then it must return a boolean value, which will determine if the item
* must be included or not in the filtered array.
*/
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 filters = {
size: (size) => size === 50 || size === 70,
color: (color) => ['blue', 'black'].includes(color.toLowerCase()),
details: (details) => details.length < 30 && details.width >= 70,
locations: (locations) => {
if (locations.includes('USA')) return true; // case sensitive
if (locations.includes('Japan')) return true; // case sensitive
const url = window.location.pathname.toLowerCase();
if (url.includes('/en-us/')) return true; // not case sensitive
if (url.includes('/es/')) return true; // not case sensitive
return false;
}
};
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);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment