简体   繁体   中英

How to filter an array of objects using all of each object's properties to check for a match?

Given a search string:

Jane

and this array of objects:

[
  {
    name: 'Jane Smith',
    address: '123 Main St, Boston, MA 01234',
    telephone: {
      primary: '1234567890',
      secondary: '1112223333'
    }
  },
  {
    name: 'John Smith',
    address: '333 Main St, New York, NY 56789',
    telephone: {
      primary: '2223334444',
      secondary: '3334445555'
    }
  },
  ...
]

we can filter the array by name:

arr.filter(person => person.name.includes(search))

Great. That works well if all we are only searching by each object's name property.

What is the best practice for filtering across all properties of an object?

Do we have to do something like:

arr.filter(person => 
  person.name.includes(search) ||
  person.address.includes(search) ||
  person.telephone.primary.includes(search) ||
  person.telephone.secondary.includes(search)
)

This becomes tedious and error prone if there are more than a couple properties.

Is there a way to filter an array if any property's value matches a search string?

Update:

This works nicely for top level properties on the person object.

.filter(person => {
  for (let property in person) {
    return String(person[property]).includes(search)
  }
})

Working on trying to find a nice solution for recursively searching through properties that may themselves be objects.

Use Object.keys and Array#some in a recursive function (I have called it deepIncludes ) to check if any property (or sub-property) includes the search string. Here I have used this to store the search string, because that allows you to filter your array of people with this syntax:

let result = array.filter(deepIncludes, 'Search String')

 function deepIncludes (object) { return Object.keys(object).some(k => { let v = object[k] return (v && typeof v == 'object') ? deepIncludes.call(this, v) : String(v).includes(this) }) } let array = [ { name: 'Jane Smith', address: '123 Main St, Boston, MA 01234', telephone: { primary: '1234567890', secondary: '1112223333' } }, { name: 'John Smith', address: '333 Main St, New York, NY 56789', telephone: { primary: '2223334444', secondary: '3334445555' } } ] // Usage: console.log( array.filter(deepIncludes, '3334445555') //=> [ { name: 'John Smith', ... } ] ) 

You can use JSON.stringify with the replacer parameter as a way to traverse the object.

function search(obj, str) {
  var found = false;

  JSON.stringify(obj, (key, value) => {
    if (!found && typeof value === 'string' && value.includes(str)) found = true;
    else return value;
  });

  return found;
}

In the end I chose something close to this:

function search (obj, text) {
  for (let prop in obj) {
    if (String(obj[prop]).includes(text)) {
      return true
    } else if (typeof obj[prop] === 'object') {
      return search(obj[prop], text)
    }
  }
}

Usage:

.filter(person => search(person, text))

Update:

The for...in method does not work well for my use case. I'm not sure why, but some keys are not searched.

Using the Object.keys method works perfectly.

function search (obj, text) {
  return Object.keys(obj).some(k => 
    typeof obj[k] === 'object'
      ? search(obj[k], text)
      : String(obj[k]).includes(text)
  )
}

(same usage)

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