Note: I'm still very new to the code-review stack exchange, so if this question does not align with the community guidelines please leave a comment below explaining and I will do my best to modify it to adhere to the community guidelines.
Explanation
I'm attempting to implement a filter()
function that functions most similarly to (I might be wrong in my understanding here) a WHERE
query on a schema-less database that only allows for AND
operators when concatenating WHERE
clauses (no OR
), and =
operators for comparison (no <
, >
, CONTAINS
, etc. operators).
Requirements
I hope this describes the functions expected behavior well enough.
The function should take in an array of objects and a filter. It should return objects within the array that are considered valid within the context of the filter. The filter is a subset of key/values that MUST be present in each object for them to be considered valid within the context of the filter.
Simple Example
Given the data:
const data = [
{
name: 'John',
age: 10
},
{
name: 'Jill',
age: 15
},
{
name: 'Jack',
age: 10
}
];
And the filter:
{
age: 10
}
The results would look like:
[
{
name: 'John',
age: 10
},
{
name: 'Jack',
age: 10
}
]
Code I'd like reviewed
Currently, my function looks like this:
Keep in mind I allow the data
to also be an object, in which case we get the data array using Object.values(data);
. This is a requirement of the project I'm implementing this function in. So, please ignore the first few lines of the function and just consider that data
is always an Array.
function filter(data, f) {
// Make sure we're dealing with an array, if an object is passed,
// just consider it's values.
let finalData;
if (Array.isArray(data)) {
finalData = data;
}
if (finalData === undefined && typeof data === 'object') {
finalData = Object.values(data);
}
if (finalData === undefined) {
throw new Error('Data being filtered must be either an Array or an Object.');
}
// Define a function to use for filtering the object. This may use recurrsion if one of
// the values in the filter is another object.
const isInFilter = (obj, f) => {
let filterTermsMet = true;
for (const filteredProp in f) {
filterTermsMet = (typeof f[filteredProp] === 'object' && !Array.isArray(f[filteredProp]))
? isInFilter(obj[filteredProp], f[filteredProp])
: obj[filteredProp] === f[filteredProp];
if (!filterTermsMet) {
break;
}
}
return filterTermsMet;
}
// Perform the filter on the final object and return the result.
return finalData.filter(element => {
return isInFilter(element, f);
});
};
Review I'm looking for / Questions I have of peers
Please bear in mind that I'm relatively new to JavaScript/Node.js.
- Am I doing this the expected way within the context of JavaScript/Node.js? I.e is there perhaps some built-in I'm unaware of that performs similar logic or could simplify my function?
- Is there a more efficient way to perform this in JavaScript?
- Is there anything my function isn't taking into consideration, missing in general or overlooking when taking this approach?
- Is it a bad idea to use recursion here for considering sub-objects in the filter?
- Any other comments / constructive criticism you might have.
More Complex Examples
Input Data
const objectsArr = [
{
name: 'John',
age: 25,
physical: {
height: 6.2,
weight: 200
}
},
{
name: 'Jason',
age: 24,
physical: {
height: 6.0,
weight: 200
}
},
{
name: 'Jenny',
age: 25,
physical: {
height: 5.3,
weight: 105
}
},
{
name: 'Kevin',
age: 21,
physical: {
height: 5.3,
weight: 189
}
},
{
name: 'Phil',
age: 24,
physical: {
height: 6.0,
weight: 189
}
}
];
Run Filters
let filterToUse;
filterToUse = { // normal condition
age: 25
};
console.log(filterToUse, filter(objectsArr, filterToUse).map(person => person.name));
filterToUse = { // sub-object condition
physical: {
weight: 200
}
};
console.log(filterToUse, filter(objectsArr, filterToUse).map(person => person.name));
filterToUse = { // alternate sub-object condition
physical: {
height: 5.3
}
};
console.log(filterToUse, filter(objectsArr, filterToUse).map(person => person.name));
filterToUse = { // sub-object condition, multiple conditions (AND)
age: 24,
physical: {
height: 6.0
}
};
console.log(filterToUse, filter(objectsArr, filterToUse).map(person => person.name));
Results
{ age: 25 } [ 'John', 'Jenny' ]
{ physical: { weight: 200 } } [ 'John', 'Jason' ]
{ physical: { height: 5.3 } } [ 'Jenny', 'Kevin' ]
{ age: 24, physical: { height: 6 } } [ 'Jason', 'Phil' ]