I have an array of object with hierarchical structure, something like this:
[
{name: 'ParentOne', children: [
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo', children: [
{name: 'ParentOneChildTwoGrandChildOne'},
]},
]},
{name: 'ParentTwo', children: [
{name: 'ParentTwoChildOne', children: [
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'}
]},
{name: 'ParentTwoChildTwo'}
]}
];
I want to flatten it:
[
{name: 'ParentOne'},
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo'},
{name: 'ParentOneChildTwoGrandChildOne'},
{name: 'ParentTwo'},
{name: 'ParentTwoChildOne'},
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'},
{name: 'ParentTwoChildTwo'}
]
I have tried _.flatten()
and _.flatMap()
, but it does not produce what I need. What is the best way to achieve it preferably using lodash.js or underscore.js.
No need for underscore/lodash.
const arr = [
{name: 'ParentOne', children: [
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo', children: [
{name: 'ParentOneChildTwoGrandChildOne'},
]},
]},
{name: 'ParentTwo', children: [
{name: 'ParentTwoChildOne', children: [
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'}
]},
{name: 'ParentTwoChildTwo'}
]}
];
function flatten(arr) {
return arr? arr.reduce((result, item) => [
...result,
{ name: item.name },
...flatten(item.children)
], []) : [];
}
console.log(flatten(arr));
Recursive functions is the way to go for any depth of iteration.
With some ES2015 and LoDash/Underscore
var arr = [{
name: 'ParentOne',
children: [{
name: 'ParentOneChildOne'
}, {
name: 'ParentOneChildTwo',
children: [{
name: 'ParentOneChildTwoGrandChildOne'
}, ]
}, ]
}, {
name: 'ParentTwo',
children: [{
name: 'ParentTwoChildOne',
children: [{
name: 'ParentTwoChildOneGrandChildOne'
}, {
name: 'ParentTwoChildOneGrandChildTwo'
}]
}, {
name: 'ParentTwoChildTwo'
}]
}];
var res = _.reduce(arr, (a, b) => {
(rec = item => {
_.each(item, (v, k) => (_.isObject(v) ? rec(v) : a.push(_.zipObject([k], [v]))))
})(b);
return a;
}, []);
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
I would use .reduce
and recursion to do this. Here's my implementation using Array.reduce, but you could do much the same with underscore's reduce function.
const arr = [
{name: 'ParentOne', children: [
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo', children: [
{name: 'ParentOneChildTwoGrandChildOne'},
]},
]},
{name: 'ParentTwo', children: [
{name: 'ParentTwoChildOne', children: [
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'}
]},
{name: 'ParentTwoChildTwo'}
]}
];
function flatten(arr) {
return arr.reduce((result, current) => {
if (current.children) {
const children = flatten(current.children);
delete current.children;
result.push(current);
result.push(...children);
} else {
result.push(current);
}
return result;
}, [])
}
console.log(flatten(arr));
You could try adapting the flatten
function given in this answer, and slightly twist the logic to your object's structure.
//Your object
var data = [{
name: 'ParentOne',
children: [{
name: 'ParentOneChildOne'
},
{
name: 'ParentOneChildTwo',
children: [{
name: 'ParentOneChildTwoGrandChildOne'
}, ]
},
]
},
{
name: 'ParentTwo',
children: [{
name: 'ParentTwoChildOne',
children: [{
name: 'ParentTwoChildOneGrandChildOne'
},
{
name: 'ParentTwoChildOneGrandChildTwo'
}
]
},
{
name: 'ParentTwoChildTwo'
}
]
}
];
//georg's flatten function
flatten = function(x, result, prefix) {
if (_.isObject(x)) {
_.each(x, function(v, k) {
flatten(v, result, prefix ? prefix + '_' + k : k)
})
} else {
result[prefix] = x
}
return result
}
//using the function on your data
result = flatten(data, {});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Does this help at all?
You could use some destruction and use a recursive function for collecting all wanted items.
var array = [{ name: 'ParentOne', children: [{ name: 'ParentOneChildOne' }, { name: 'ParentOneChildTwo', children: [{ name: 'ParentOneChildTwoGrandChildOne' },] },] }, { name: 'ParentTwo', children: [{ name: 'ParentTwoChildOne', children: [{ name: 'ParentTwoChildOneGrandChildOne' }, { name: 'ParentTwoChildOneGrandChildTwo' }] }, { name: 'ParentTwoChildTwo' }] }],
flat = (r, { name, children = [] }) => [...r, { name }, ...children.reduce(flat, []) ],
result = array.reduce(flat, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
On EDGE, you need a different use of default values.
var array = [{ name: 'ParentOne', children: [{ name: 'ParentOneChildOne' }, { name: 'ParentOneChildTwo', children: [{ name: 'ParentOneChildTwoGrandChildOne' },] },] }, { name: 'ParentTwo', children: [{ name: 'ParentTwoChildOne', children: [{ name: 'ParentTwoChildOneGrandChildOne' }, { name: 'ParentTwoChildOneGrandChildTwo' }] }, { name: 'ParentTwoChildTwo' }] }],
flat = (r, { name, children }) => [...r, { name }, ...(children || []).reduce(flat, []) ],
result = array.reduce(flat, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }