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
3 Answers
Reset to default 4As 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 }
]*/