javascript - How to filter array of objects with filter with dynamic properties count? - Stack Overflow

I have an array of objects with some bool and int properties:let employees = [{ isSkilled: false, isLe

I have an array of objects with some bool and int properties:

let employees = [
  { isSkilled: false, isLeader: false, departmentHeadType: 0 },
  { isSkilled: true, isLeader: false, departmentHeadType: 1 },
  { isSkilled: true, isLeader: true, departmentHeadType: 2 }
]

And there is a select where user can check options (in any combinations) by which employees will be filtered:

So after selection I have a filter something like that:

//if user selected "Skilled", "Leaders" and "Heads of departments"
var filter1 = { isSkilled: true, isLeader: true, departmentHeadType: 2 };
//if user selected "Skilled", "Newbie" (so I simply omit isSkilled property in filter
//as both variants are ok), "Leaders", "Heads of departments" and "Deputy heads of departments"
var filter2 = { isLeader: true, departmentHeadType: [1, 2] };

For filter1 I can use the following code:

const filtered = employees.filter(object => JSON.stringify(filter1) == JSON.stringify(object));

But JSON.stringify works only if compared objects have the same properties in the same order. And it will not work for filter2 where isSkilled is omitted and departmentHeadType is array. For filter2 the only idea I have is several ifs and specific properties filtering in each case (that approach is ugly and and not scalable as employee properties grow and thus filter options increase).

Is there a better and more generic aproach for this?

I have an array of objects with some bool and int properties:

let employees = [
  { isSkilled: false, isLeader: false, departmentHeadType: 0 },
  { isSkilled: true, isLeader: false, departmentHeadType: 1 },
  { isSkilled: true, isLeader: true, departmentHeadType: 2 }
]

And there is a select where user can check options (in any combinations) by which employees will be filtered:

So after selection I have a filter something like that:

//if user selected "Skilled", "Leaders" and "Heads of departments"
var filter1 = { isSkilled: true, isLeader: true, departmentHeadType: 2 };
//if user selected "Skilled", "Newbie" (so I simply omit isSkilled property in filter
//as both variants are ok), "Leaders", "Heads of departments" and "Deputy heads of departments"
var filter2 = { isLeader: true, departmentHeadType: [1, 2] };

For filter1 I can use the following code:

const filtered = employees.filter(object => JSON.stringify(filter1) == JSON.stringify(object));

But JSON.stringify works only if compared objects have the same properties in the same order. And it will not work for filter2 where isSkilled is omitted and departmentHeadType is array. For filter2 the only idea I have is several ifs and specific properties filtering in each case (that approach is ugly and and not scalable as employee properties grow and thus filter options increase).

Is there a better and more generic aproach for this?

Share Improve this question edited Mar 7 at 6:33 bairog asked Mar 7 at 6:02 bairogbairog 3,4097 gold badges44 silver badges57 bronze badges 1
  • Why not try to create a function to check if the item value is an array or not before returning the actual filtered data? – Keyboard Corporation Commented Mar 7 at 6:19
Add a comment  | 

3 Answers 3

Reset to default 4

As you noted, JSON.stringify() is not a great option because you cannot reliably guarantee object property order.

You could make it dynamic by iterating the filter object entries (key / value pairs) and creating predicate functions for each, comparing the filter value against the object value for the same key.

You'd need a little extra logic to detect arrays and use an includes check.

const filterRecords = (array, filter) => {
  const predicates = Object.entries(filter).map(
    ([key, val]) =>
      (obj) =>
        Array.isArray(val) ? val.includes(obj[key]) : val === obj[key],
  );

  return array.filter((e) => predicates.every((p) => p(e)));
};

let employees = [
  { isSkilled: false, isLeader: false, departmentHeadType: 0 },
  { isSkilled: true, isLeader: false, departmentHeadType: 1 },
  { isSkilled: true, isLeader: true, departmentHeadType: 2 }
];

//if user selected "Skilled", "Leaders" and "Heads of departments"
var filter1 = { isSkilled: true, isLeader: true, departmentHeadType: 2 };
//if user selected "Skilled", "Newbie" (so I simply omit isSkilled property in filter
//as both variants are ok), "Leaders", "Heads of departments" and "Deputy heads of departments"
var filter2 = { isLeader: true, departmentHeadType: [1, 2] };

console.log('filter1', filterRecords(employees, filter1));
console.log('filter2', filterRecords(employees, filter2));
.as-console-wrapper { max-height: 100% !important; }

Already have the best answer, but i will drop this just in case you need it.

let employees = [
  { isSkilled: false, isLeader: false, departmentHeadType: 0 },
  { isSkilled: true, isLeader: false, departmentHeadType: 1 },
  { isSkilled: true, isLeader: true, departmentHeadType: 2 }
];

function filterFunction(items, filter) {
  return items.filter(item => {
    return Object.keys(filter).every(key => {

      // If the filter value is an array, check if the item value is included in the array. Else check only for equality
      return Array.isArray(filter[key]) ? filter[key].includes(item[key]) : item[key] == filter[key];
    });
  });
}

var filter1 = { isSkilled: true, isLeader: true, departmentHeadType: 2 };
var filter2 = { isLeader: true, departmentHeadType: [1, 2] };

console.log('filtered1', filterFunction(employees, filter1)); // Output: [{ isSkilled: true, isLeader: true, departmentHeadType: 2 }]
console.log('filtered2', filterFunction(employees, filter2)); // Output: [{ isSkilled: true, isLeader: true, departmentHeadType: 2 }]

// Sample data
const arr = [
  { isSkilled: false, isLeader: false, departmentHeadType: 0 },
  { isSkilled: true, isLeader: false, departmentHeadType: 1 },
  { isSkilled: true, isLeader: true, departmentHeadType: 2 }
];

// Filter criteria examples
const filter1 = { isSkilled: true, isLeader: true, departmentHeadType: 2 };
const filter2 = { isLeader: true, departmentHeadType: [1, 2] };

/**
 * Extends Array prototype to support dynamic filtering.
 * @param {Object} filter - An object representing the filtering criteria.
 * @returns {Array} - Filtered array based on the criteria.
 */
Array.prototype.dynamicFilter = function (filter) {
    const filterHash = Object.keys(filter).map(key => {
        return {
            key,
            value: filter[key]
        }
    });
    
    const res = this.filter(obj => {
        let isMatch = true;
        filterHash.forEach(filter => {
            if (Array.isArray(filter.value)) {
                isMatch = filter.value.includes(obj[filter.key]);
            } else if (obj[filter.key] !== filter.value) {
                isMatch = false;
            }
        });
        return isMatch;
    });
    return res;
};

// Test cases
console.log(arr.dynamicFilter(filter1)); 
// Expected output: [{ isSkilled: true, isLeader: true, departmentHeadType: 2 }]
console.log(arr.dynamicFilter(filter2)); 
/* Expected output: [
  { isSkilled: true, isLeader: false, departmentHeadType: 1 },
  { isSkilled: true, isLeader: true, departmentHeadType: 2 }
]*/

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744945651a4602599.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信