admin管理员组文章数量:1415139
I'm implementing search functionality into my application. The search results in the UI are returned based on an array of objects. Essentially what I'm trying to do is iterate over the name, custNumber, and sneak values in each object, and only return the objects that contain a value that includes a string (generated from the users search bar). The idea is that users can search for anything in the object and yield the correct results
here is my 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 how far I've gotten
return result.filter(convo => {
return convo.name.toLowerCase().includes(searchbarVal.toLowerCase())
})
The obvious problem here is that this only is only returning objects based on the name value. However, I need it to pare the name, custNumber, and sneak values in each object to the users search. I've tried forEach, object.values, and object.entries methods and haven't been able to get them to work. Any help here is much appreciated!!
I'm implementing search functionality into my application. The search results in the UI are returned based on an array of objects. Essentially what I'm trying to do is iterate over the name, custNumber, and sneak values in each object, and only return the objects that contain a value that includes a string (generated from the users search bar). The idea is that users can search for anything in the object and yield the correct results
here is my 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 how far I've gotten
return result.filter(convo => {
return convo.name.toLowerCase().includes(searchbarVal.toLowerCase())
})
The obvious problem here is that this only is only returning objects based on the name value. However, I need it to pare the name, custNumber, and sneak values in each object to the users search. I've tried forEach, object.values, and object.entries methods and haven't been able to get them to work. Any help here is much appreciated!!
Share Improve this question asked May 25, 2018 at 23:41 mgarland3mgarland3 211 silver badge2 bronze badges 1- Check this question and answer. It may help. stackoverflow./questions/34398279/… – Blast Commented Mar 3, 2019 at 23:06
5 Answers
Reset to default 7recursive search
This is a topic I've written about recently. Here is a generic deepFind
. It works recursively and can "search" any input value.
Below we construct a simple set of data
and then show how deepFind
can search the data and return matches
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
)
Above deepFind
only works by matching values directly using ===
. Because it accepts a higher-order function f
however, we can specialize its behavior in meaningful ways.
string match using deepFind
Below we encode our generic string-matching search
function using 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
Verify the results in your own browser
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
returning multiple search results
The program above only returns the first match. If you wanted to return all of the results, we can do so using generators, which are perfectly suited for this task
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)
}
}
Now we implement searchAll
using our new generator
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)
// []
Run searchAll
in your browser below
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
( 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 (searchAll ("81", result))
// [ { custNumber: '8812654434', ... }
// , { custNumber: '8908198230', ... }
// ]
console.log (searchAll ("Du", result))
// [ { name: "Ron Duluth", ... } ]
console.log (searchAll ("zzz", result))
// []
case insensitive search
Above, our search
function uses v .includes (query)
but because we're working with a higher-order function, we can make the behaviour as specific as we want.
Using searchAll
as an example, we could change it like below
const searchAll = (query = "", data = {}) =>
Array.from
( deepFindAll
( o =>
Object.values (o) .some (v =>
String (v) === v && v .includes (query))
String (v) === v
&& v .toLowerCase () .includes (query .toLowerCase ()))
, data
)
)
But that's making a plete mess of our function. It's time to abstract a little more and explain what we're doing by giving our intentions names
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
( anyString (v =>
caseInsenstiveMatch (v, query))
, data
)
)
Isolating behaviors and defining separate functions is an important part of writing good programs. Showing search
and searchAll
side-by-side highlights this importance. The new helpers anyString
and caseInsensitiveSearch
keep the code clear, but also make it easier to reuse behaviours where needed.
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
Higher-order functions have all sorts of uses for keeping our code clean and descriptive. Below, we define dead-simple versions of match
and lower
. Then using contramap
, we bring our program together.
The emphasis here is on the simplicity of each function. A simple function is easier to test, easier to debug, and easier to bine with other simple functions. The Unix philosophy, "Do one thing and do it well" should be ringing in your ears right now
const contramap = (f, g) =>
(x, y) => f (g (x), g (y))
const match = (x = "", y = "") =>
x .includes (y)
const lower = (x = "") =>
x .toLowerCase ()
const caseInsenstiveMatch =
contramap (match, lower)
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 other powers that may not be immediately obvious. If it interests you, I remend Monoidal Contravariant Functors are actually useful! by Brian Lonsdorf. Don't let the title scare you; the author has a knack for making this stuff easy.
A 'some' in your filter might do the trick, checking all the keys.
return result.filter(convo => {
return Object.keys(convo).some(key => {
return convo[key].toLowerCase().includes(searchbarVal.toLowerCase())
})
})
function searchObj(search){
let answer = [];
result.forEach(re => {
if(JSON.stringify(re).indexOf(search) > 0){
answer.push(re)
}
});
return answer;
}
Loop through every element of the array, convert them into a string and use indexOf
to find the matching criteria. That way you can save a few loops without looping each and every key of every element.
Try
let search= result.filter(x=> ['name','custNumber','sneak']
.reduce((o,a)=> x[a].toLowerCase().includes(query.toLowerCase())||o, false) );
Where query
is your searchbarVal.toLowerCase()
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,
}
]
let query="89"; // searchbarVal.toLowerCase()
let search= result.filter(x=> ['name','custNumber','sneak'].reduce((o,a)=> x[a].toLowerCase().includes(query.toLowerCase())||o, false) );
console.log(search);
You can loop through the object and try and do something like the following:
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,
}
];
var searchStr = "Donna";
console.log(searchObj(searchStr));
function searchObj(search){
var searchResult = [];
for(var obj in result){
var str = JSON.stringify(result[obj]);
if(str.indexOf(search) > 0){
searchResult.push(result[obj]);
}
}
return searchResult;
}
本文标签:
版权声明:本文标题:javascript - In an array of objects, returns objects where ANY value matches a specific string - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745221468a2648414.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论