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.
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']));
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
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.