Reputation: 4199
I have an array like this
const treeObj = [
{
id: 1,
name: 'Section One',
items: [
{ id: 1, name: 'Section One Item One' },
{ id: 2, name: 'Section One Item Two' },
{ id: 3, name: 'Section One Item Three' },
],
},
{
id: 2,
name: 'Section Two',
items: [
{ id: 1, name: 'Section Two Item One' },
{ id: 2, name: 'Section Two Item Two' },
{ id: 3, name: 'Section Two Item Three' },
],
},
{
id: 3,
name: 'Section Three X',
items: [
{ id: 1, name: 'Section Two Item One' },
{ id: 2, name: 'Section Two Item Two' },
{ id: 3, name: 'Section Two Item Three' },
],
},
{
id: 4,
name: 'Section Four X',
items: [
{ id: 1, name: 'Section Two Item One' },
{ id: 2, name: 'Section Two Item Two' },
{ id: 3, name: 'Section Two Item Three' },
],
},
{
id: 5,
name: 'Section Three X',
items: [
{ id: 1, name: 'Section Two Item One' },
{ id: 2, name: 'Section Two Item Two' },
{ id: 3, name: 'Section Two Item Three' },
],
},
{
id: 6,
name: 'Section Four X',
items: [
{ id: 1, name: 'Section Two Item One' },
{ id: 2, name: 'Section Two Item Two' },
{ id: 3, name: 'Section Two Item Three' },
],
},
];
I need to write a search functionality that searches the array with an input. I need to check whether string typed is matching each the outer name or any of the name inside items in each section.
Like: str === treeObj[0].name or str in any one of treeObj .items[].name
I need the output in same structure. I'm familiar with filter. But i cant find the right way to do this, checking both section name and item names.
Search the array with a string whether it matches eahc section naem or any one of names in items inside
Can someone point me in the right direction?
UPDATE
I tried this
const searchStr = searchText.toLowerCase();
filteredData = data.filter(obj => {
if (obj.name.toLowerCase().includes(searchStr)) {
return true;
}
if (obj.items.find(item => item.name.toLowerCase().includes(searchStr))) {
return true;
}
return false;
});
But it returns all items in a section. doesn't filter each section. I need to search to work for both section and items (Try searching an item like: Section One Item One)
Upvotes: 0
Views: 263
Reputation: 4199
filteredData = data
.filter((element, index) => {
const target = isSectionMatching.findIndex(item => item.id === element.id);
isSectionMatching[target].match = element.name.toLowerCase().includes(searchStr);
return (
element.items.some(subElement => subElement.name.toLowerCase().includes(searchStr)) ||
isSectionMatching[index].match
);
})
.map((element, index) => {
const target = isSectionMatching.findIndex(item => item.id === element.id);
if (isSectionMatching[target].match) {
return element;
}
return {
...element,
items: element.items.filter(subElement => subElement.name.toLowerCase().includes(searchStr)),
};
});
} else {
filteredData = [...data];
}
I had a custom requirement, so i was able to find a solution by combining all the answers i got to this question
Thanks to All contributors
Upvotes: 1
Reputation: 9002
You can use a combination of find
and filter
:
const str = "Section One Item One"
const filter = treeObj.filter(obj => {
if(obj.name == str) {
return true
} else if(obj.items.find(item => item.name == str)) {
return true
} else {
return false;
}
})
console.log(filter)
Here is a codepen with the code.
EDIT I updated my original algorithm to better fit your needs:
const str = "Section One Item One"
let res = null
const filter = treeObj.some(obj => {
if(obj.name == str) {
res = obj;
return true
}
const item = obj.items.find(item => item.name == str);
if(item) {
res = {...obj, items: [item] }
return true
}
return false;
});
console.log(res);
As suggested in the comments by guyjob, I now use some
to improve speed. When it finds a match, the search will be aborted. As some only returns true
or false
, I created a new res
variable, which will contain the result of the search. In case of a match it will set res
to the whole item if the name was found or filter all but the found element, when it was one of the subitems. I also updated the codepen.
Upvotes: 3
Reputation: 5318
You can filter it out by matching name:
var treeObj = [ { id: 1, name: 'Section One', items: [ { id: 1, name: 'Section One Item One' }, { id: 2, name: 'Section One Item Two' }, { id: 3, name: 'Section One Item Three' }, { id: 3, name: 'Section One Item Four' } ], }, { id: 2, name: 'Section Two', items: [ { id: 1, name: 'Section Two Item One' }, { id: 2, name: 'Section Two Item Two' }, { id: 3, name: 'Section Two Item Three' }, ], }, { id: 3, name: 'Section Three X', items: [ { id: 1, name: 'Section Two Item One' }, { id: 2, name: 'Section Two Item Two' }, { id: 3, name: 'Section Two Item Three' }, ], }, { id: 4, name: 'Section Four X', items: [ { id: 1, name: 'Section Two Item One' }, { id: 2, name: 'Section Two Item Two' }, { id: 3, name: 'Section Two Item Three' }, ], }, { id: 5, name: 'Section Three X', items: [ { id: 1, name: 'Section Two Item One' }, { id: 2, name: 'Section Two Item Two' }, { id: 3, name: 'Section Two Item Three' }, ], }, { id: 6, name: 'Section Four X', items: [ { id: 1, name: 'Section Two Item One' }, { id: 2, name: 'Section Two Item Two' }, { id: 3, name: 'Section Two Item Three' }, ], }, ];
var strToSearch = 'Four';
var result = treeObj.filter(k=>k.name.includes(strToSearch) || k.items.some(s=>s.name.includes(strToSearch)));
console.log(result);
Upvotes: 1