secretgspot
9/28/2019 - 6:59 AM

Filters an array of objects with multiple match-criteria.

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);
  });
});