Reputation: 159
I am having a multi level object of not sure how many levels it can have for example I am having a following object
[{
"id": "1234",
"desc": "sample1",
"items": [{
"item1": "testItem1",
"item2": "testItem2"
}]
},
{
"id": "3456",
"desc": "sample2",
"items": [{
"item1": "testItem12",
"item2": "testItem23"
}]
}
]
How can we do a deep search for a string in pure javascript so that if the search string matches any of the values in the above object should return its respective object level.
for example If I search for "sample" it should return both the objects and if I search for "1234" it should return only one object. Is there a way we can search for the string with out passing the key and value pair we are searching? search string can be any where in the object. please help me with the logic.
lets say if I search with either "sample" or "testItem1" It should return me both the object from the parent level as it matches in both and If I search for "testItem23" then it should return me the complete 2nd object from the parent level.
Upvotes: 6
Views: 7599
Reputation: 147473
If you need to deal with values other than arrays and strings, you'll need to test the values before comparing. You might compare the value passed in so you only test property values with the right type.
This just returns objects that are elements of the original data array. If you want some other object, you'll need to provide the logic.
var arr = [{
"id": "1234",
"desc": "sample1",
"items": [{
"item1": "testItem1",
"item2": "testItem2"
}]
},
{
"id": "3456",
"desc": "sample2",
"items": [{
"item1": "testItem12",
"item2": "testItem23"
}]
}
];
function findInObjArray(array, value) {
var found = [];
// Helper to search obj for value
function findInObj(obj, value) {
return Object.values(obj).some(
v =>
// If v is an object, call recursively
typeof v == 'object' && v != 'null'? findInObj(v, value) :
// If string, check if value is part of v
typeof v == 'string'? v.indexOf(value) >= 0 :
// Check numbers, make NaN == NaN
typeof v == 'number'? v === value || isNaN(v) && isNaN(value):
// Otherwise look for strict equality: null, undefined, function, boolean
v === value
);
};
array.forEach(function(obj) {
if (findInObj(obj, value)) found.push(obj);
})
return found;
}
console.log(
findInObjArray(arr, 'testItem23')
);
Upvotes: 1
Reputation: 18525
Here is another approach which utilizes js only.
The idea is to filter the main array and inside for each object to flatten the values via recursion. Then use another filter with indexOf
to match the actual property value inside the flattened values array ...
var data = [{ "id": "1234", "desc": "sample1", "items": [{ "item1": "testItem1", "item2": "testItem2", "item3": [{ "item1": "testItemA", "item2": "testItemB" }] }] }, { "id": "3456", "desc": "sample2", "items": [{ "item1": "testItem12", "item2": "testItem23" }] } ]
const flattenValues = (o, values = []) => !(Object.values(o).forEach(x => (Array.isArray(x) || typeof x == 'object') ? [...flattenValues(x, values)] : values.push(x))) && values
const textSearch = (v, d) => d.filter(x => flattenValues(x).filter(y => y.indexOf(v) >= 0).length)
console.log(textSearch('sample', data))
console.log(textSearch('1234', data))
console.log(textSearch('testItem1', data))
console.log(textSearch('testItem12', data))
More details:
The flattenValues
function for example would return for the object with id 1234
an array of the flattened values like this for each object:
['1234', 'sample1', 'testItem1', 'testItem2' ...]
Then the textSearch
would use filter
and inside compare if the filter of the result of:
flattenValues(x).filter(y => y.indexOf(v) >= 0)
returns truthy
value
Upvotes: 1
Reputation: 57185
Here's a simple iterative approach using a stack to walk your structure and build the result array:
const find = (needle, haystack) => {
const result = [];
const stack = [[haystack, haystack]];
while (stack.length) {
const [curr, parent] = stack.pop();
for (const o of curr) {
for (const k in o) {
if (Array.isArray(o[k])) {
stack.push([o[k], o]);
}
else if (o[k].includes(needle)) {
result.push("items" in o ? o : parent);
}
}
}
}
return result;
};
const data = [{
"id": "1234",
"desc": "sample1",
"items": [{
"item1": "testItem1",
"item2": "testItem2"
}]
},
{
"id": "3456",
"desc": "sample2",
"items": [{
"item1": "testItem12",
"item2": "testItem23"
}]
}
];
console.log(find("sample", data));
console.log(find("1234", data));
console.log(find("testItem12", data));
Upvotes: 4