0

I want to build a filter that depending on how many rules there are, it filters an arrya of objects based on those rules.

I have an array of objects like this:

const myList = [
{name: 'Joe',
 sex: 'male',
 isStared: false,
},
{name: 'Ann',
 sex: 'female',
 isStared: true,
},
{name: 'Gil',
 sex: 'female',
 isStared: true,
},
] 

I also have an object with the rules to fillter by which the user specifies, for example it can be:

const rules = {sex: 'male', isStared: 'false'}

I dont want to hard code it so that it specifically checks for sex === 'male' or isStared === true

But I want, that if there are more are less rules, it checks for those and returns only those that for example are male and are stared.

What i have right now is a hard coded filtering, but if the rules change, it will break:

myList.filter(friend => friend.sex === action.filterQuery.sex && friend.sex.isStared === action.filterQuery.sex)

Any idea how to achieve this?

Thanks.

  • 2
    Does this answer your question? [Filter array of objects with another array of objects](https://stackoverflow.com/questions/31005396/filter-array-of-objects-with-another-array-of-objects) – devlin carnate Apr 08 '20 at 21:02

2 Answers2

2

You can use filter, Object.entries, and every -

const myList = [
{name: 'Joe',
 sex: 'male',
 isStared: false,
},
{name: 'Ann',
 sex: 'female',
 isStared: true,
},
{name: 'Gil',
 sex: 'female',
 isStared: true,
},
] 

const rules = {sex: 'male', isStared: false}  // !

const result =
  myList.filter(item =>
    Object.entries(rules).every(([ k, v ]) =>
      item[k] === v
    )
  )

console.log(result)
// [
//   {
//     "name": "Joe",
//     "sex": "male",
//     "isStared": false
//   }
// ]

!: And watch out, 'false' is not the same as false. Don't wrap booleans in quotes.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    `Object.entries(someObj)` gives an array of `[ key, value ]` pairs. For example `Object.entries({ a: 1, b: 2 })` gives `[['a',1],['b',2]]` – Mulan Apr 08 '20 at 21:16
0
const filter_with_ruleset = ruleset => input_arr => input_arr.filter(
  item => Object.entries(ruleset)
    .reduce((accum, curr) => accum && item[curr[0]] === curr[1], true)
);

// Usage
const ruleset_1 = { sex: 'male', isStared: true };
const filter_starred_males = filter_with_ruleset(ruleset_1);

console.log(filter_starred_males(myList));

BUT this only filters by equality, not like by "name.starts_with" or something.

Also just watch out, your original data has a spelling error, it looks like isStared should be isStarred, but as long as you're consistent you'll be fine.

dmitrydwhite
  • 776
  • 3
  • 11
  • Using `reduce` means you will continue iterating through `ruleset` entries even after you found a `false` result. Ie, no need to `accum && ... && ... && ...`, after `accum` becomes `false` just once, the result will always be `false`. `Array.prototype.every` and `Array.prototype.some` have the short-circuit behaviour we're looking for in this case. – Mulan Apr 08 '20 at 21:19