0

Lets assume I have an array of objects:

let users = [{
 name: "Mark",
 location: "US",
 job: "engineer"
},
{
 name: "Mark",
 location: "US",
 job: "clerk"
},
{
 name: "Angela",
 location: "Europe",
 job: "pilot"
}, 
{
 name: "Matthew",
 location: "US",
 job: "engineer"
}]

and I have a filter object with all categories I want to filter data against (there can be multiple values per key):

const filters = {
  name: ["Mark", "Matthew"],
  location: ["US"],
  job: ["Engineer"]
}

Based on these filters and data the expected result would return:

[{name: "Mark", location: "US", job: "Engineer"}, {name: "Matthew", location: "US", job: "Engineer"}]

I have tried filtering with:

users.filter(user => {
  for(let k in filters) {
    if(user[k] === filters[k]) {
      return true;
    }
  }
})

however, this method doesn't take into account that a filter category might contain more than one value which I can handle by doing like:

filters[k][0] or filters[k][1]

but it isn't dynamic.

If anyone has any input that would be much appreciated! Thank you.

3
  • 1
    filters[k].includes(user[k]) ? Commented Apr 9, 2021 at 22:55
  • instead of users[k] ===filters[k] try filters[k].includes(users[k]) which will look for similarities in the whole array.. I'm writing this from my phone though and can be a tiny bit off in the syntax Commented Apr 9, 2021 at 22:59
  • @Jonas heh beat me to it! Best answer actually Commented Apr 9, 2021 at 23:00

2 Answers 2

2

Use Object.entries() on filters to get an array of [key, values] pairs. Iterate the pairs with Array.every(), and check that each pair includes the value of the current object.

const fn = (arr, filters) => {
  const filtersArr = Object.entries(filters)
  
  return arr.filter(o => 
    filtersArr.every(([key, values]) => 
      values.includes(o[key])
    )
  )
}

const users = [{"name":"Mark","location":"US","job":"engineer"},{"name":"Mark","location":"US","job":"clerk"},{"name":"Angela","location":"Europe","job":"pilot"},{"name":"Matthew","location":"US","job":"engineer"}]

const filters = {
  name: ["Mark", "Matthew"],
  location: ["US"],
  job: ["engineer"]
}

const result = fn(users, filters)

console.log(result)

One caveat of using Array.includes() is that differences in case would provide a false answer (Engineer and engineer in this case). To solve that convert the current word to a RegExp, with the Case-insensitive search flag (i), and check using Array.some() if it fits any of the words in the array.

const fn = (arr, filters) => {
  const filtersArr = Object.entries(filters)
  
  return arr.filter(o => 
    filtersArr.every(([key, values]) => {
      const pattern = new RegExp(`^${o[key]}$`, 'i')
    
      return values.some(v => pattern.test(v))
    })    
  )
}

const users = [{"name":"Mark","location":"US","job":"engineer"},{"name":"Mark","location":"US","job":"clerk"},{"name":"Angela","location":"Europe","job":"pilot"},{"name":"Matthew","location":"US","job":"engineer"}]

const filters = {
  name: ["Mark", "Matthew"],
  location: ["US"],
  job: ["Engineer"]
}

const result = fn(users, filters)

console.log(result)

Sign up to request clarification or add additional context in comments.

Comments

1

You can loop over the entries of the filters object and ensure that the value of each key is one of the allowed ones.

let users = [{name:"Mark",location:"US",job:"Engineer"},{name:"Mark",location:"US",job:"clerk"},{name:"Angela",location:"Europe",job:"pilot"},{name:"Matthew",location:"US",job:"Engineer"}];
const filters = {
  name: ["Mark", "Matthew"],
  location: ["US"],
  job: ["Engineer"]
};
const res = users.filter(o => 
  Object.entries(filters).every(([k,v])=>v.includes(o[k])));
console.log(res);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.