Filtering Array Based On Value In Deeply Nested Object In Javascript
Solution 1:
Here is an ES6 solution based on reduce
, filter
and Object.assign
:
functionfilterTree(topics, find) {
return topics.reduce(function (acc, topic) {
const sub_categories = topic.sub_categories.reduce(function (acc, cat) {
const indicators = cat.indicators.filter( ind => ind.name.includes(find) );
return !indicators.length ? acc
: acc.concat(Object.assign({}, cat, { indicators }));
}, []);
return !sub_categories.length ? acc
: acc.concat(Object.assign({}, topic, { sub_categories }));
}, []);
}
// sample dataconst topics = [
{
"id": 1,
"name": "topic title 1",
"sub_categories": [
{
"id": 1,
"name": "category title 1",
"indicators": [
{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
},
{
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}
]
},
{
"id": 6,
"name": "category title 6",
"indicators": [
{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}
]
}
]
},
{
"id": 2,
"name": "topic title 2",
"sub_categories": [
{
"id": 2,
"name": "category 2",
"indicators": [
{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}
]
},
{
"id": 4,
"name": "category 4",
"indicators": [
{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}
]
}
]
}
];
// Call the functionvar res = filterTree(topics, 'foo');
// Output resultconsole.log(res);
.as-console-wrapper { max-height: 100%!important; top: 0; }
Solution 2:
You could use an iterative and recursive approach for filtering the given array, without hard wired properties.
constdeepFilter = (array, indicator) => {
return array.filter(functioniter(o) {
returnObject.keys(o).some(k => {
if (typeof o[k] === 'string' && o[k].includes(indicator)) {
returntrue;
}
if (Array.isArray(o[k])) {
o[k] = o[k].filter(iter);
return o[k].length;
}
});
});
}
const topics = [{ id: 1, name: "topic title 1", sub_categories: [{ id: 1, name: "category title 1", indicators: [{ id: 1, name: "indicator 1", sub_category_id: 1 }, { id: 7, name: "indicator 7 - foo", sub_category_id: 1 }] }, { id: 6, name: "category title 6", indicators: [{ id: 8, name: "indicator 8", sub_category_id: 6 }] }] }, { id: 2, name: "topic title 2", sub_categories: [{ id: 2, name: "category 2", indicators: [{ id: 2, name: "indicator 2 - foo", sub_category_id: 2 }] }, { id: 4, name: "category 4", indicators: [{ id: 5, name: "indicator 5", sub_category_id: 4 }] }] }];
console.log(deepFilter(topics, 'foo'));
.as-console-wrapper { max-height: 100%!important; top: 0; }
Solution 3:
This can pretty much all be done with ES 5 array methods (no library or polyfill needed for IE 9+):
var passed = topics.filter(function(x) {
return x.subcategories.some(function(y) {
return y.indicators.some(function(z) {
returnBoolean(z.name.match(/foo/));
});
});
});
While this is total one-off code, the situation is perhaps too complicated for an easily digestible general-purpose solution (although I'd love to see someone prove me wrong).
UPDATE
After taking a closer look at the output you will need to use reduce
instead of filter:
var passed = topics.reduce((acc, x) => {
var hasfoo = x.subcategories.reduce((accum, y) => {
var ls = y.indicators.filter(z => z.name.match(/foo/));
if (ls.length) {
accum.push(Object.assign({}, y, {indicators: ls}));
}
return accum;
}, []);
if (hasfoo.length) {
acc.push(Object.assign({}, x, {subcategories: hasfoo}));
}
return acc;
}, []);
Astute readers will note the recursive pattern here. Abstracting that out is left as an exercise, I'm tapped out. Object.assign
will need to be polyfilled for old browsers (trivial though).
Solution 4:
this will also modify existing topics
var result = topics.filter(top =>
(top.sub_categories = top.sub_categories.filter(cat =>
(cat.indicators = cat.indicators.filter(i => i.name.match(/foo/))).length)
).length
);
Example
var topics = [{
"id": 1,
"name": "topic title 1",
"sub_categories": [{
"id": 1,
"name": "category title 1",
"indicators": [{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
}, {
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}]
}, {
"id": 6,
"name": "category title 6",
"indicators": [{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}]
}]
}, {
"id": 2,
"name": "topic title 2",
"sub_categories": [{
"id": 2,
"name": "category 2",
"indicators": [{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}]
}, {
"id": 4,
"name": "category 4",
"indicators": [{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}]
}]
}];
var result = topics.filter(top => (top.sub_categories = top.sub_categories.filter(cat => (cat.indicators = cat.indicators.filter(i => i.name.match(/foo/))).length)).length);
console.log(result);
Solution 5:
One more implementation.
topics.forEach(function(topic, indexTopic, indexTopicArray) {
topic.sub_categories.forEach(function(subCat, indexsubCat, arraysubCat) {
subCat.indicators = subCat.indicators.filter(indic => indic.name.includes("foo"));
if(subCat.indicators.length === 0) {
indexTopicArray[indexTopic].sub_categories.splice(indexsubCat, 1);
}})});
console.log(topics);
Complete Code.
var topics = [
{
"id": 1,
"name": "topic title 1",
"sub_categories": [
{
"id": 1,
"name": "category title 1",
"indicators": [
{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
},
{
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}
]
},
{
"id": 6,
"name": "category title 6",
"indicators": [
{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}
]
}
]
},
{
"id": 2,
"name": "topic title 2",
"sub_categories": [
{
"id": 2,
"name": "category 2",
"indicators": [
{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}
]
},
{
"id": 4,
"name": "category 4",
"indicators": [
{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}
]
}
]
}
];
topics.forEach(function(topic, indexTopic, indexTopicArray) {
topic.sub_categories.forEach(function(subCat, indexsubCat, arraysubCat) {
subCat.indicators = subCat.indicators.filter(indic => indic.name.includes("foo"));
if(subCat.indicators.length === 0) {
indexTopicArray[indexTopic].sub_categories.splice(indexsubCat, 1);
}})});
console.log(topics);
Post a Comment for "Filtering Array Based On Value In Deeply Nested Object In Javascript"