1
\$\begingroup\$

The below code filters my contacts according to a search term the user typed.

Is there a way I can avoid explicitly writing all the object keys but keep the readability?

  contacts = contacts.filter(
    contact =>
      contact.firstName.toLowerCase().includes(search) ||
      contact.lastName.toLowerCase().includes(search) ||
      contact.title.toLowerCase().includes(search) ||
      contact.type.toLowerCase().includes(search) ||
      contact.notes.toLowerCase().includes(search) ||
      contact.phones.filter(val =>
        val.value.toLowerCase().includes(search)
      ).length ||
      contact.emails.filter(val =>
        val.value.toLowerCase().includes(search)
      ).length
  )
\$\endgroup\$
2
  • \$\begingroup\$ When you say "dynamic", are you looking for a way to make this code more flexible (i.e. usable in other contexts)? If so, could you expound a bit on what you're looking for? \$\endgroup\$ – Scotty Jamison Jun 1 at 3:50
  • \$\begingroup\$ @ScottyJamison I meant to avoid explicitly writing all the object keys \$\endgroup\$ – user3378165 Jun 1 at 4:30
0
\$\begingroup\$

You can use Object.entries(obj) and Array.isArray(obj) to make it generic:

let testObj = obj => obj.toLowerCase().includes(search);
contacts = contacts.filter(
  contact => Object.entries(contact).some(entry => {
    let [key, value] = entry;
    if (Array.isArray(value)) {
      return value.some(val => testObj(val.value));
    } else {
      return testObj(value);
    }
  });
);

If you run the code above with the following sample data:

let contacts = [
  {
    phones: [
        {value: "8765"}, 
        {value: "4356"}
    ],
    foo: "hey",
      bar: "adggg"
  },
  {
    phones: [
        {value: "1234"}, 
        {value: "3456"}
    ],
    foo: "oh",
    bar: "sdff"
  },
  {
    phones: [
        {value: "8765"}, 
        {value: "4356"}
    ],
    foo: "ah",
    bar: "hhh"
  } 
];
let search = "ah";

you get:

{
  phones: [
      {value: "8765"}, 
      {value: "4356"}
  ],
  foo: "ah",
  bar: "hhh"
} 
\$\endgroup\$
1
  • 3
    \$\begingroup\$ Your code will not run if used in modules or strict mode. Always declare variable. \$\endgroup\$ – Blindman67 Jun 1 at 2:43
0
\$\begingroup\$

I can think of a few ways:

  1. Serialize the whole object. A simple version: JSON.stringify(contact).toLowerCase().includes(search) This will absolutely get the wrong answer if the user searches for quotes, colons or braces, but if this is not a concern, it will be easy.

  2. To be a little more precise, you're going to have to do a little "meta-programming" to get the names of the fields of your object. Object.keys(contract) will give you an array of all the keys, which you can then iterate through. You'll need to partition these into two groups: those that are simple strings, and those that are nested arrays. You can then re-work the code you have to use these more generic lists.

  3. As your code grows, this will probably be your best path: Create a list of the valid field names; this "data dictionary" can be shared in such a way that they can be used here in a generic way. It could be something as simple as SEARCHABLE_CONTACT_FIELDS = ['firstName','lastName','title'... or a more complex SEARCHABLE_CONTACT_FIELDS = { firstName: 'String', lastName: 'String', phone: 'Array', ... This gives you more control and this "meta" information will be useful for other functions as well.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.