Find all objects in an array of objects that contain at least one value that matches a given string

I am currently integrating search functionality in my application. The UI search results are generated from an array of objects. My goal is to loop through the name, custNumber, and sneak values in each object and display only the ones that contain a substring entered by the user in the search bar. Users should be able to search for any value within the object and receive accurate results.

Here is the array:

var result = [{
  name: 'Donna Shomaker',
  custNumber: '6658924351',
  sneak: 'string1 string1 string1',
  foo: false,
  bar: false,
},
{
  name: 'Ron Duluth',
  custNumber: '8812654434',
  sneak: 'string2 string2 string2',
  foo: false,
  bar: false,
},
{
  name: 'Jimmy Dawson',
  custNumber: '8908198230',
  sneak: 'string3 string3 string3',
  foo: false,
  bar: false,
}
]

This is what I have so far:

return result.filter(convo => {
  return convo.name.toLowerCase().includes(searchbarVal.toLowerCase())
})

The issue here is that it only filters based on the name field. I need it to compare not just the name but also the custNumber and sneak values in each object with the user's search term. I've attempted using forEach, Object.values(), and Object.entries() methods without success. Any assistance on this matter would be greatly appreciated!

Answer №1

recursive search

This particular subject was recently discussed in a recent post. Here is an implementation of a general-purpose function called deepFind, which performs recursive searches on any input value.

Below, we present a basic dataset called data and showcase how the deepFind function can be used to search for specific matches within the data.

const data =
  [ { a: 1, b: 1 }
  , { a: 2, b: 2, c: { d: [ { e: 2 } ] } }
  , { a: 3, b: { c: { d: { e: { f: 3 } } } } }
  ]

const deepFind = (f, obj = {}) =>
{ if (Object (obj) === obj)
  { if (f (obj) === true)
      return obj

    for (const [ k, v ] of Object.entries (obj))
    { const res =
        deepFind (f, v)

      if (res !== undefined)
        return res
    }
  }

  return undefined
}

console.log
  ( deepFind (x => x.a === 1, data)             // { a: 1, b: 1 }
  , deepFind (x => x.e === 2, data)             // { e: 2 }
  , deepFind (x => Array.isArray(x.d), data)    // { d: [ { e: 2 } ] }
  , deepFind (x => x.f === 3, data)             // { f: 3 }
  , deepFind (x => x.e && x.e.f === 3, data)    // { e: { f: 3 } }
  , deepFind (x => x.z === 9, data)             // undefined
  )

The functionality of deepFind is limited to direct matches using ===. By utilizing a higher-order function f, however, we can customize its behavior in more meaningful ways.

string match with deepFind

Here, we demonstrate a specialized string-matching function named search that leverages the capabilities of deepFind:

const search = (query = "", data) =>
  deepFind
    ( o =>
        Object.values (o) .some (v =>
          String (v) === v && v .includes (query))
    , data
    )

search ("D", result)
// { name: "Donna Shomaker", ... }

search ("Du", result)
// { name: "Ron Duluth", ... }

search ("ng3", result)
// { name: "Jimmy Dawson", sneak: "string3 string3 string3", ... }

search ("zzz", result)
// undefined

Feel free to test the results in your own browser environment

const deepFind = (f, obj = {}) =>
{ if (Object (obj) === obj)
  { if (f (obj) === true)
      return obj
      
    for (const [ k, v ] of Object.entries (obj))
    { const res =
        deepFind (f, v)
        
      if (res !== undefined)
        return res
    }
  }
  
  return undefined
}

const search = (query = "", data) =>
  deepFind
    ( o =>
        Object.values (o) .some (v =>
          String (v) === v && v .includes (query))
    , data
    )

const result =
  [ { name: 'Donna Shomaker'
    , custNumber: '6658924351'
    , sneak: 'string1 string1 string1'
    , foo: false
    , bar: false
    }
  , { name: 'Ron Duluth'
    , custNumber: '8812654434'
    , sneak: 'string2 string2 string2'
    , foo: false
    , bar: false
    }
  , { name: 'Jimmy Dawson'
    , custNumber: '8908198230'
    , sneak: 'string3 string3 string3'
    , foo: false
    , bar: false
    }
  ]

console.log (search ("D", result))
// { name: "Donna Shomaker", ... }

console.log (search ("Du", result))
// { name: "Ron Duluth", ... }

console.log (search ("ng3", result))
// { name: "Jimmy Dawson", sneak: "string3 string3 string3", ... }

console.log (search ("zzz", result))
// undefined

fetching multiple search results

The previous program only outputs the first matching result. If you need all matches, using generators would be a suitable approach. Let's see how it can be done by introducing a generator function.

const deepFindAll = function* (f, o = {})
{ if (Object (o) === o)
  { if (f (o) === true)
      yield o
    for (const [ _, v ] of Object.entries (o))
      yield* deepFindAll (f, v)
  }
}

Next, we create searchAll based on our newly defined generator function:

const searchAll = (query = "", data = {}) =>
  Array.from
    ( deepFindAll
        ( o =>
            Object.values (o) .some (v =>
              String (v) === v && v .includes (query))
        , data
        )
    )

searchAll ("81", result)
// [ { custNumber: '8812654434', ... }
// , { custNumber: '8908198230', ... }
// ]

searchAll ("Du", result)
// [ { name: "Ron Duluth", ... } ]

searchAll ("zzz", result)
// []

Execute searchAll in your browser to observe the outcomes

const deepFindAll = function* (f, o = {})
{ if (Object (o) === o)
  { if (f (o) === true)
      yield o
    for (const [ _, v ] of Object.entries (o))
      yield* deepFindAll (f, v)
  }
}

const searchAll = (query = "", data = {}) =>
  Array.from
    ( deepFindAll
       ...
...
  ]
  
console.log (searchAll ("81", result))
// [ { custNumber: '8812654434', ... }
// , { custNumber: '8908198230', ... }
// ]

console.log (searchAll ("Du", result))
// [ { name: "Ron Duluth", ... } ]

console.log (searchAll ("zzz", result))
// []

case insensitive search

In the aforementioned search function, matching is performed with v .includes (query). However, exploiting a higher-order function allows us to specify behavior as needed.

An alternative method involving searchAll could look like this:

const searchAll = (query = "", data = {}) =>
  Array.from
    ( deepFindAll
        ( o =>
            Object.values (o) .some (v =>
              String (v) === v
                <b>&& v .toLowerCase () .includes (query .toLowerCase ())</b>
        , data
        )
    )

This modification may seem convoluted. It's crucial to abstract and clearly communicate our intentions by defining separate functions for each operation.

const anyString = f => o =>
  Object.values (o) .some (v =>
    String (v) === v && f (v))

const caseInsenstiveMatch = (x, y) =>
  x.toLowerCase () .includes (y.toLowerCase ())
  
const searchAll = (query = "", data = {}) =>
  Array.from
    ( deepFindAll
        ( <b>anyString (v =>
            caseInsenstiveMatch (v, query))</b>
        , data
        )
    )

Separating behaviors and creating distinct functions are fundamental aspects of developing quality software. Comparing search and searchAll side by side underscores the importance of clarity and modularity. The introduction of helper functions like anyString and caseInsensitiveSearch not only enhances code readability but also promotes reusability where necessary.

const search = (query = "", data) =>
  deepFind
    ( anyString (v =>
        caseInsenstiveMatch (v, query))
    , data
    )

const searchAll = (query = "", data = {}) =>
  Array.from
    ( deepFindAll
        ( anyString (v =>
            caseInsenstiveMatch (v, query))
        , data
        )
    )

contramap

Utilizing higher-order functions offers numerous benefits in terms of code organization and clarity. Below, we define straightforward match and lower functions. Through the use of contramap, we unite these functions seamlessly.

The key emphasis here lies in keeping each function simple and focused. A concise function is easier to validate, troubleshoot, and integrate with other similar functions. The Unix philosophy of "Do one thing and do it well" should resonate strongly at this point.

const contramap = (f, g) =>
  (x, y) => f (g (x), g (y))

<b>const match = (x = "", y = "") =>
  x .includes (y)

const lower = (x = "") =>
  x .toLowerCase ()

const caseInsenstiveMatch =
  contramap (match, lower)</b>

const anyString = (f) => (o = {}) =>
  Object.values (o) .some (v =>
    String (v) === v && f (v))

const searchAll = (query = "", data = {}) =>
  Array.from
    ( deepFindAll
        ( anyString (v =>
            caseInsenstiveMatch (v, query))
        , data
        )
    )

Contramap unlocks additional potential that might not be immediately apparent. For those intrigued by this concept, I recommend delving into Monoidal Contravariant Functors are actually useful! by Brian Lonsdorf. Despite its title, the author excels at simplifying seemingly complex concepts.

Answer №2

To enhance your filter, consider using a 'some' to scan through all keys for potential matches.

return result.filter(convo => {
  return Object.keys(convo).some(key => {
     return convo[key].toLowerCase().includes(searchbarVal.toLowerCase())
  })
})

Answer №3

function filterBySearchTerm(searchTerm){
  let searchResults = [];
  data.forEach(item => {
    if(JSON.stringify(item).indexOf(searchTerm) >= 0){
      searchResults.push(item)
    }
  });
  return searchResults;
}

Iterate through each element of the dataset, convert them to a string, and utilize indexOf method to identify relevant items based on the search term. This approach optimizes the process by avoiding unnecessary iterations through every key of all elements.

Answer №4

Give it a shot

let searchResults = result.filter(item => ['name','custNumber','sneak']
     .reduce((acc, key)=> item[key].toLowerCase().includes(query.toLowerCase()) || acc, false) );

Make sure to use your searchbarVal.toLowerCase() as the value for query

var resultArray = [{
  name: 'Donna Shomaker',
  custNumber: '6658924351',
  sneak: 'string1 string1 string1',
  foo: false,
  bar: false,
},
{
  name: 'Ron Duluth',
  custNumber: '8812654434',
  sneak: 'string2 string2 string2',
  foo: false,
  bar: false,
},
{
  name: 'Jimmy Dawson',
  custNumber: '8908198230',
  sneak: 'string3 string3 string3',
  foo: false,
  bar: false,
}
]

let query="89"; // searchbarVal.toLowerCase()

let searchResults = resultArray.filter(item => ['name','custNumber','sneak'].reduce((acc, key)=> item[key].toLowerCase().includes(query.toLowerCase()) || acc, false) );

console.log(searchResults);

Answer №5

If you want to search through the object and perform a specific action, you can iterate through it as shown in the example below:

var data = [{
  name: 'Donna Shomaker',
  custNumber: '6658924351',
  sneak: 'string1 string1 string1',
  foo: false,
  bar: false,
},
{
  name: 'Ron Duluth',
  custNumber: '8812654434',
  sneak: 'string2 string2 string2',
  foo: false,
  bar: false,
},
{
  name: 'Jimmy Dawson',
  custNumber: '8908198230',
  sneak: 'string3 string3 string3',
  foo: false,
  bar: false,
}
];

var searchTerm = "Donna";

console.log(findObject(searchTerm));

function findObject(term){
  var searchResults = [];
  for(var obj in data){
      var strData = JSON.stringify(data[obj]);
      if(strData.indexOf(term) > 0){
        searchResults.push(data[obj]);
      }
  }
  return searchResults;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for creating a dashed border line that animates in a clockwise direction when hovering

I currently have a dashed border around my div element: .dash_border{ border: dashed; stroke: 2px; margin:20px; } My goal is to make the dashed lines move clockwise when my pointer hovers over the div element and stop moving ...

Error in sending data to the server via the specified URL: "Http failure response for http://localhost/post.php: 0 Unknown Error" and POST request to http://localhost/post.php failed with error code

Feeling a bit stuck trying to add data to my database. As a junior with PHP and Angular, I am using PHP via XAMPP and Angular 8. Is it possible to create separate files for the post and get methods in the PHP file? app.component.ts import { Component, O ...

In what way can an item be "chosen" to initiate a certain action?

For example, imagine having two containers positioned on the left and right side. The left container contains content that, when selected, displays items on the right side. One solution could involve hiding elements using JavaScript with display: none/in ...

Clicking on an element triggers the addition of a class, and the newly

Once the page has finished loading, I am able to utilize jQuery to add a specific class in the following way. $(document).ready(function() { $("#parent").click(function(){ $(".child").addClass("active"); }); }) ...

Is it necessary to make a distinct route for SocketIO implementation?

I am new to Socket.IO and I recently completed the chat tutorial in the documentation along with some additional tasks to gain a better understanding of how it works. Currently, I am attempting to establish a connection between a NodeJS server and a React ...

How to extract a subset of data from an existing object and create a new object in Vue JS

My latest project involves creating a search app to find products. I have implemented a PHP script that returns results in JSON format when a keyword is submitted. The data retrieval is done using axios. The challenge I am facing is with handling the disp ...

Exploring the power of Jade and Angular through implementing a for loop within a table structure

I'm brand new to using Jade and Angular, and I could really use a hint from someone more experienced. ... - for (var i = 0; i < p.length; i++) tr td= i + 1 td= price(value='p[i].somedbstuff') ... I want the la ...

Capture various data points in an array with each click

I am currently working on a menu of buttons where users can select multiple options at the same time. My goal is to record all selected buttons in one array instead of individual arrays for each button. The end result I hope to achieve is an array like t ...

What is an alternative way to use mobx-react 'observer' that does not involve the decorator syntax?

I've been working on integrating `mobx` into a virtual reality application built with React 360. Initially, I attempted to use the decorator syntax without success, so I decided to switch to the non-decorator syntax. While going through the mobx docum ...

Pop-up message on card selection using JavaScript, CSS, and PHP

I have a total of 6 cards displayed in my HTML. Each card, when clicked, should trigger a modal window to pop up (with additional information corresponding to that specific card). After spending a day searching for a solution online, I've come here s ...

Could the issue be related to a bug in the combination of ng-repeat and ngInclude?

I've been experimenting with loading different templates in this manner: <div ng-class="{active:$first,in:$first,'tab-pane':true}" id="{{p.path}}_settings" ng-repeat="p in panes" ng-include="buildPath(p.path)"> </div> Here&apos ...

Interactive form found on a webpage

Hey there! I'm currently working on a task where I want a form to be displayed when the edit button is clicked. Once the save button in the form is pressed, I want to update my database with the new information. It's crucial that this process hap ...

Activate the event when the radio button is changed

Is there a way to change the selected radio button in a radio group that is generated using a for loop? I am attempting to utilize the changeRadio function to select and trigger the change of the radio button based on a specific value. <mat-radio-group ...

SyntaxError: Unexpected token : error caused by an invalid label

When attempting to perform an ajax call, I receive a JSON object as a response: { id: 6, success: "true" } The ajax call in question is the following: window.foobar = function(foo){ $.ajax({ url: "http://foobar.com/sites/foo/", ...

Steps to apply V-model to elements

I am currently facing an issue with giving dynamically created input fields dynamic v-models from JSON data using vue.js. Here's what I'm doing: new Vue({ el: '#app', data() { return { totalAmt: 500, paymentMode: ...

changing the breadcrumb item to a dropdown item in a dynamic way

Hey there, I'm currently working on creating a breadcrumb item using Angular. Here is what I have so far: https://i.stack.imgur.com/zt5yX.png I decided to add a dropdown for the breadcrumb item, but I'm facing a challenge: I want to be able to ...

What is the proper way to define an array of objects in TypeScript?

In search of a way to define a function that accepts an array of unspecified object types, known as "anonymous types." I want to enforce strict typing with TypeScript. The objects in the array all have properties like name, price, and description. bagTotal ...

A step-by-step guide on adding items from the shopping cart and checkout page to the database specifically for cash on

Currently, I am in the process of developing an e-commerce website. One issue that I have encountered is regarding the checkout page, which appears identical to the cart page. My goal is to retrieve all product details including quantity selected, pricing, ...

Emit Observables within deeply nested callback functions

Hey there! I'm currently working on a web app using Angular2/4 and I've encountered an issue with Observables. My goal is to call a function within a component and then have some code executed once that function completes. Here's the releva ...

ng-if is executed prior to the specified time

This unique Soundcloud player was created using Plangular, a directive that utilizes AngularJS and the Soundcloud API. Due to certain restrictions with third party apps not being able to stream some Soundcloud tracks, I have implemented ng-if="track" and n ...