0

Trying to wrap my brain around how I should tackle filtering an array of objects and only returning results that satisfy all of the tags.

The tags could be any of the fields - fname/lname/email/position/etc

let search_tags = ['CEO', 'Steven'];

let contacts = [
  { fname: 'Steve', lname: 'Johnson', email: 'user@domain.com', position: 'CEO' },
  { fname: 'James', lname: 'Laurence', email: 'boss@domain.com', position: 'CFO' }
]

let results = contacts.filter((contact) => {
  if (search_tags.includes(contact.fname) || 
      search_tags.includes(contact.lname) ... ) {
    return contact;
  }
}

I shortened a bit of the code for brevity and obviously this solution will return contacts that match any search_tag but... I need to only return results that satisfy every search_tag.

It's been a long day and I have no one to talk this through so I'm hoping someone may be able to point me in the right direction or give me that ah-ha! moment I'm hoping for :)

Thanks in advance!

theAlexandrian
  • 870
  • 6
  • 18
Chad Garrett
  • 9
  • 1
  • 2
  • use && rather than || – Jaromanda X Mar 01 '18 at 02:26
  • If you are just looking to be pointed in the right direction: turn search_tags into a `Set` object then for each object in contacts, create a set of the field values, then do a set intersection or symmetric difference. Let us know if you need help doing that. – Ray Toal Mar 01 '18 at 02:27

3 Answers3

0

If you wanted to return one that matched every search tag you'd want to use && instead of || but that still leaves you with a bunch of verbose and duplicated code

Instead of operating directly on the contact Object, you can use Object.values() https://mdn.io/objectvalues which would give you an array of ['steve', 'johnson', 'user@domain]... etc.

Then you could in your filter:

contacts.filter((contact) => {
    const contactValues = Object.values(contact);
    // Return the search item if at least one item matches
    // Would return true if at least one item matches
    return contactValues.some(value => search_tags.includes(value));
    // return true only if all search tags match
    return contactValues.every(value => search_tags.includes(value));
}

Object.values is quite a new feature, so if you don't have it available in babel, then you can use Object.keys and grab the value using contact[someKey]

3stacks
  • 1,880
  • 1
  • 16
  • 21
0

Array.prototype.filter() can be combined with Array.prototype.every(), Object.values() and Array.prototype.includes() to construct an Array of matches consisting solely of contact Objects that contain a matching value for every element in search_tags.

See below for a practical example.

// Search Tags.
const search_tags = ['CEO', 'Steven']

// Contacts.
let contacts = [
  { fname: 'Steven', lname: 'Johnson', email: 'user@domain.com', position: 'CEO' },
  { fname: 'James', lname: 'Laurence', email: 'boss@domain.com', position: 'CFO' }
]

// Matches.
const matches = contacts.filter((contact) => search_tags.every((tag) => Object.values(contact).includes(tag)))

// Log.
console.log(matches)
Arman Charan
  • 5,669
  • 2
  • 22
  • 32
0

ES6:

function filterIt(arr, searchKeys) {
  return arr.filter(obj => Object.keys(obj).some(key => searchKeys.includes(obj[key])));
}
huan feng
  • 7,307
  • 2
  • 32
  • 56