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

Is the contenteditable feature working properly when updating PHP, SQL, and AJAX in the network?

Struggling with updating modified text to SQL using PHP and Ajax. Unsure if the issue lies in the data sent through Ajax or a problem within the PHP script. Below is my snippet from the HTML file: <tr> <td class="editingTab" contenteditable=&ap ...

Tips for transforming a string into an object using AngularJS

Here is a string I'm working with: $scope.text = '"{\"firstName\":\"John\",\"age\":454 }"'; I am trying to convert it into a JavaScript object: $scope.tmp = {"firstName":"John","age":454 }; Please note: J ...

Utilizing PHP for Interactive JavaScript Modals

Encountering an issue with my JavaScript. The modal initially functions correctly, but upon clicking it for the second time, two modals appear - one from the previous click and another new one. The stacking of modals continues each time the button is cli ...

Extract the content of a textbox within an iframe located in the parent window

Is it possible to retrieve the value of a text box in an iframe from the parent page upon clicking a button? Below is an example code snippet showcasing the situation: <div> <iframe src="test.html" > <input type=text id="parent_text"> & ...

Arrange fixed-position elements so that they adhere to the boundaries of their adjacent siblings

Is there a way to keep two fixed elements aligned with their sibling element on window resize? <div class="left-img"> IMAGE HERE </div> <!-- fixed positioned --> <div class="container"> Lorem ipsum... </div> <div class=" ...

Is it possible to perform nested arrays joining using Php::PDO?

It may seem like a lot to ask, but I'm wondering if it's possible without having to code it myself. I have two tables: "users (ID, USERNAME)" and "pictures (ID, USER_ID, NAME)". To retrieve images of users, the query would usually be: SELECT * F ...

Encountered an error while attempting to update an object: Unable to read property 'push' of undefined

Encountering an issue while attempting to update an object with additional information, receiving an error message stating 'property \'push\' of undefined'. /*Below is the object model in question:*/ export class Students { ...

Error: Unable to iterate through the {(intermediate value)}. It's not a function

Snippet of the original code: const genreOptions = [{{ genreOptions | json_encode | raw }}].map((type , label) => ({value: type, label: label})); Piece of debugging code: const genreOptions = { "Horror": "Kork ...

Perform an asynchronous reload of DataTables using ajax.reload() before calling a function

I need help extracting real-time data from a datatable and populating a form for editing when an edit button is clicked on each row. Despite my efforts, the ajax.reload() function doesn't load the table in time to fill the form with correct data. The ...

Using Sequelize to Include Model Without Specifying Table Name

I am new to Sequelize I am facing an issue with "Nested Eager Loading" I have 2 tables with a one-to-many relationship Comment Table User Table This is the code I am using for the query Comment.findAll({ include: [User] }) The result I got was ...

How can you determine if an API method call has completed in Angular and proceed to the next task?

Two methods are being used for api calls in my code. Method one is calling out method two and needs to wait for method two's api call to finish before continuing with its own process. I attempted to achieve this using the complete function inside a su ...

What is the best method for performing cross-domain queries utilizing ajax and jsonp?

When attempting to send an ajax request to a specific URL, I encountered an error. Below is the code I used: $.ajax({ url: "http://webrates.truefx.com/rates/connect.html?q=ozrates&c=EUR/USD&f=csv&s=n", dataType : 'jsonp', ...

React | Utilizing ForwardedRefs with React Components

I'm currently working on a React project where I am creating a custom component that needs to be exported using forwardedRef. However, as I attempt to do this, an error keeps popping up: error This is the code snippet causing the issue: export inter ...

"Is there a way to adjust the range slider to display currency instead of

I stumbled upon this amazing slider on codepen. Can someone guide me on how to adjust it to display a range from €500 to €6000 while keeping the vibrant red background? I've attempted various solutions like: <input id = "range" type = "range ...

Utilize tags as properties in Vue.js by selecting them

I am attempting to retrieve a value from a select tag and use it in an object property. Below is the HTML code: <div id="app"> <h3>{{title}}</h3> <div class="form"> <div class="form-group"> <div ...

Transform an array of integers into a mapping of values and corresponding IDs for selected values

I've been able to successfully load values from my API into react-select for single select, but I'm encountering some issues with multi-select. const fetch_companies = () => { API.get("/companies") ... data = data.map(({ id: ...

Transforming JavaScript to TypeScript in Angular: encountering error TS2683 stating that 'this' is implicitly of type 'any' due to lacking type annotation

While in the process of migrating my website to Angular, I encountered an error when attempting to compile the JS into TS for my navbar. After searching around, I found similar issues reported by other users, but their situations were not quite the same. ...

What are the steps for utilizing ckeditor to send textarea information via ajax?

Here is the code snippet that controls the textarea in my chat application: <div class="chat"> <div class="messages"></div> <textarea class="entry" name="entry" placeholder="Welcome to the Chat. Enter your message here!">&l ...

Tips for circumventing CORS in an ajax request using various browser extensions or add-ons

I am encountering a cross-origin problem with my WCF service. Despite adding an extension in chrome to bypass CORS, the issue persists. Is there any other extension/add-on/plugin available to test the service locally and bypass the CORS problem? Edit Aft ...

How to create a Bootstrap panel that collapses on mobile devices and expands on desktop screens?

Is there a simple way to remove the "in" class from the div with id "panel-login" when the screen size is less than 1199px (Bootstrap's xs, sm, and md modes)? I believe JavaScript or JQuery could be used for this, but I'm not very proficient in e ...