简体   繁体   中英

filter an arrays of objects from an array of strings

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM