Reputation: 9230
I'm aware there are similar questions but none have been able to help me thus far - filtering an array of objects from and array of strings relies on you knowing the key value pair you would like to match, and same with this question here
Say I have an array of objects like so..
let users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike';
},
favouriteFood: 'apples'
},
{
name: 'Steve',
age: 32,
pets null
favouriteFood: 'icecream'
},
{
name: 'Jason',
age: 31,
pets: null
favouriteFood: 'tacos'
},
{
name: 'Jason',
age: 31,
pets: {
cat: 'Jerry'
},
favouriteFood: 'bread'
},
]
now I would like to be able to filter the array of users by matching a string in any of the objects keys. For example I want to filter out anyone whos name isnt 'steve' - keep in mind I may also want to filter out anyone who isnt 42 or who's favourite food isn't 'apples'
filter(term) {
return objects.filter(x => {
for(let key of Object.keys(x)) {
if(typeof(x[key]) === 'object') {
return JSON.stringify(x[key]).toLowerCase().includes(t);
} else {
return x[key].toString().toLowerCase().includes(t);
}
}
});
}
now this function works but for only a single filter term
so If I ran filter('steve')
I would then get
users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike';
},
favouriteFood: 'apples'
},
{
name: 'Steve',
age: 32,
pets null
favouriteFood: 'icecream'
}
]
as my result, but what If I wanted to filter out steve whos favourite food is apples?
I have tried to update my function as follows to loop through an array of terms and filter according to all the strings in the array
So I have tried
function filter(terms) {
return term.forEach((t) => {
return objects.filter(x => {
for(let key of Object.keys(x)) {
if(typeof(x[key]) === 'object') {
return JSON.stringify(x[key]).toLowerCase().includes(t);
} else {
return x[key].toString().toLowerCase().includes(t);
}
}
});
});
but when I run filter(['steve', 'apples'])
I get undefined
My desired result would be
users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike';
},
favouriteFood: 'apples'
}
]
I'm not entirely sure what I'm doing wrong or how I could fix this function so it works correctly.
Any help would be appreciated.
Upvotes: 3
Views: 99
Reputation: 10627
Sorry for the late response. I kept trying to come up with recursive code that would work in almost any situation. I eventually found some really cool snippets here, which is where I derived the similar
function from equals
, with slight considerations for compatibility.
function similar(a, b){
if(a === b){
return true;
}
if(a instanceof Date && b instanceof Date){
return a.getTime() === b.getTime();
}
if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')){
return a === b;
}
if (a === null || a === undefined || b === null || b === undefined || a.prototype !== b.prototype){
return false;
}
return Object.keys(b).every(function(k){
return similar(a[k], b[k]);
});
}
let users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike'
},
favouriteFood: 'apples'
},
{
name: 'Steve',
age: 32,
pets: null,
favouriteFood: 'icecream'
},
{
name: 'Jason',
age: 31,
pets: null,
favouriteFood: 'tacos'
},
{
name: 'Jason',
age: 31,
pets: {
cat: 'Jerry'
},
favouriteFood: 'bread'
}
]
var testObj = {name:'Jason', age: 31, pets:{cat:'Jerry'}};
for(var i=0,u,l=users.length; i<l; i++){
u = users[i];
if(similar(u, testObj)){
console.log('contains testObj');
console.log(u);
}
else if(!similar(u, {pets:null}) && !similar(u, {pets:{dog:'spot'}})){
console.log('not spot');
console.log(u);
}
}
similar
will see if anything matches exactly that is not an Object, or if it is an Object it will see if a
contains b
, considering that a
and b
have properties and values at the same depth and b
doesn't consist of properties that do not exist in a
.
Upvotes: 1
Reputation: 370759
Filter by whether .every
value in the array of values needed is included in the Object.values
of a given user:
const filter = arrOfValsNeeded => users.filter(user => {
const vals = Object.values(user).map(val => typeof val === 'string' ? val.toLowerCase() : val);
return arrOfValsNeeded.every(needed => vals.includes(needed.toLowerCase()));
});
let users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike'
},
favouriteFood: 'apples'
},
{
name: 'Steve',
age: 32,
pets: null,
favouriteFood: 'icecream'
},
{
name: 'Jason',
age: 31,
pets: null,
favouriteFood: 'tacos'
},
{
name: 'Jason',
age: 31,
pets: {
cat: 'Jerry'
},
favouriteFood: 'bread'
},
]
console.log(filter(['steve', 'apples']));
Or, if you need to recursively find all primitive values:
const allPrimitives = obj => {
const primitives = [];
JSON.stringify(obj, (key, val) => {
if (typeof val !== 'object' || val === null) {
primitives.push(typeof val === 'string' ? val.toLowerCase() : val);
}
return val;
});
return primitives;
};
const filter = arrOfValsNeeded => users.filter(user => {
const vals = allPrimitives(user);
return arrOfValsNeeded.every(needed => vals.includes(needed.toLowerCase()));
});
let users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike'
},
favouriteFood: 'apples'
},
{
name: 'Steve',
age: 32,
pets: null,
favouriteFood: 'icecream'
},
{
name: 'Jason',
age: 31,
pets: null,
favouriteFood: 'tacos'
},
{
name: 'Jason',
age: 31,
pets: {
cat: 'Jerry'
},
favouriteFood: 'bread'
},
]
console.log(filter(['steve', 'apples']));
If you need partial matches as well, use vals.some
instead of vals.includes
so you can identify substrings:
const allStrings = obj => {
const strings = [];
JSON.stringify(obj, (key, val) => {
if (typeof val === 'string') {
strings.push(val.toLowerCase());
}
return val;
});
return strings;
};
const filter = arrOfValsNeeded => {
const lowerVals = arrOfValsNeeded.map(str => str.toLowerCase());
return users.filter(user => {
const existingStrings = allStrings(user);
return lowerVals.every(
lowerNeeded => existingStrings.some(
existingString => existingString.includes(lowerNeeded)
)
);
});
};
let users = [
{
name: 'Steve',
age: 42,
pets: {
dog: 'spike'
},
favouriteFood: 'apples'
},
{
name: 'Steve',
age: 32,
pets: null,
favouriteFood: 'icecream'
},
{
name: 'Jason',
age: 31,
pets: null,
favouriteFood: 'tacos'
},
{
name: 'Jason',
age: 31,
pets: {
cat: 'Jerry'
},
favouriteFood: 'bread'
},
]
console.log(filter(['steve', 'apples']));
Upvotes: 5