Reputation: 13
I'm trying to filter a JSON in JavaScript on all levels. I want to keep the "name" of each of the entries at each level based on if it is present in an array. If it isn't present at a certain level, continue through the children.
Accepted names:
var accepted = ["top", "first", "three"]
Original JSON:
{
"name": "top",
"children": [{
"name": "first",
"children": [{
"name": "second",
"children": [{ "name": "three" }]
}, {
"name": "second",
"children": [{ "name": "three" }]
}, {
"name": "second",
"children": [{ "name": "three" }]
}]
}, {
"name": "first",
"children": [{
"name": "second",
"children": [{ "name": "three" }]
}, {
"name": "second",
"children": [{ "name": "three" }]
}, {
"name": "second",
"children": [{ "name": "three" }]
}]
}]
}
New JSON:
{
"name": "top",
"children": [{
"name": "first",
"children": [
{ "name": "three" },
{ "name": "three" },
{ "name": "three" }
]
}, {
"name": "first",
"children": [
{ "name": "three" },
{ "name": "three" },
{ "name": "three" }
]
}]
}
What I've done:
var previous = ""
function loop(a) {
if (previous.name == a.name) {
previous = a
previous['true_children'] = []
}
if (accepted.includes(a.name)) {
previous['true_children'].push(a)
if (a.children != null) {
previous = a
previous['true_children'] = []
}
}
Array.isArray(a.children) && a.children.forEach(loop);
}
My overall thinking for accomplishing this was to create a true_children
property that could be iterated through the JSON rather than children
. This new property could then only contain the filtered levels from the JSON.
However, my code seems to append the first
once to true_children
at the first level and again at the second level. Is there a fix for this or a better method to accomplishing this?
Upvotes: 0
Views: 350
Reputation: 21130
Here is another recursive solution, that is hopefully not to hard to understand.
var accepted = ["top", "first", "three"],
data = {"name": "top", "children": [{"name": "first", "children": [{"name": "second", "children": [{"name": "three"}]}, {"name": "second", "children": [{"name": "three"}]}, {"name": "second", "children": [{"name": "three"}]}]}, {"name": "first", "children": [{"name": "second", "children": [{"name": "three"}]}, {"name": "second", "children": [{"name": "three"}]}, {"name": "second", "children": [{"name": "three"}]}]}]};
function transform(accepted, data) {
const children = data.children || [],
result = { name: data.name, children: [] },
isAccepted = accepted.includes(data.name);
children.forEach(child => {
child = transform(accepted, child);
const children = Array.isArray(child) ? child : [child];
result.children.push(...children);
});
// Guard against data that isn't accepted.
if (!isAccepted) return result.children;
// Delete the children key if the array is empty.
if (!children.length) delete result.children;
return result;
}
console.log(transform(accepted, data));
Upvotes: 0
Reputation: 22876
For JSON string, the filtering can be done during parsing :
var accepted = ["top", "first", "three"], json = '{"name":"top","children":[{"name":"first","children":[{"name":"second","children":[{"name":"three"}]},{"name":"second","children":[{"name":"three"}]},{"name":"second","children":[{"name":"three"}]}]},{"name":"first","children":[{"name":"second","children":[{"name":"three"}]},{"name":"second","children":[{"name":"three"}]},{"name":"second","children":[{"name":"three"}]}]}]}'
var obj = JSON.parse(json, (k, v) => k == 'name' && !accepted.includes(v) ? void 0 :
v.children && v.children.length === 1 && !v.name ? v.children[0] : v)
console.log( obj )
Upvotes: 1
Reputation: 31815
I got your desired output with the following solution, hope that helps!
const accepted = [ "top", "first", "three"];
const data = { "name": "top",
"children": [
{
"name": "first",
"children": [
{
"name": "second",
"children":[
{
"name": "three"
}
]
},
{
"name": "second",
"children":[
{
"name": "three"
}
]
},
{
"name": "second",
"children":[
{
"name": "three"
}
]
}
]
},
{
"name": "first",
"children": [
{
"name": "second",
"children":[
{
"name": "three"
}
]
},
{
"name": "second",
"children":[
{
"name": "three"
}
]
},
{
"name": "second",
"children":[
{
"name": "three"
}
]
}
]
}
]
}
function buildOutput(input, accepted) {
const filtered = Object.keys(input).reduce((accumulator, currentValue) => {
if(currentValue === 'name' && accepted.find(x => input[currentValue] === x)) {
accumulator[currentValue] = input[currentValue];
if(input['children']){
accumulator['children'] = input['children'].map(x => buildOutput(x, accepted)).filter(x => x);
if(!accumulator['children'].length){
accumulator['children'] = flattenChildren(input['children']).map(x => buildOutput(x, accepted)).filter(x => x);
}
}
}
return accumulator;
}, {});
return Object.keys(filtered).length ? filtered : null;
}
function flattenChildren(children) {
return children.reduce((accumulator, currentValue) => accumulator.concat(currentValue.children), []);
}
console.log(buildOutput(data, accepted));
Upvotes: 0