Reputation: 30421
I've been trying to create a generic function which could flatten an array of objects but am failing at every turn. JS isn't my home language. Does anyone know of any existing function which could accept an array of nested objects and output a flattened one?
Input:
const arr = [
{path:'/foo', component: SomeComponent, children: [
{path:'/one', component: SomeComponent},
{path:'/two', component: SomeComponent},
{path:'/three', component: SomeComponent},
]},
{path: '/bar', component: SomeComponent}
]
Expected output:
const flattened_arr = [
{path:'/foo', component: SomeComponent},
{path:'/foo/one', component: SomeComponent},
{path:'/foo/two', component: SomeComponent},
{path:'/foo/three', component: SomeComponent},
{path:'/bar', component: SomeComponent},
]
Upvotes: 1
Views: 6792
Reputation: 618
I recently had to solve this problem to create a nested dropdown and here is my solution in case some one need to have the parents history tracked to do things like
and also be able to send back to the Back-End every ID that is necessary
I am sharing the pure flatten and flatten on steroids version to record the parents
P.P.S. the code can easily be reused to create "path" just concat the data you need instead of keeping it an array as I had to do.
const items = [
{ id: 1, title: 'one', children: [{ id: 3, title: 'one`s child', children: [] }] },
{ id: 2, title: 'two', children: [] },
{
id: 4,
title: 'three',
children: [
{
id: 5,
title: 'three`s child',
children: [
{
id: 6,
title: 'three`s grandchild',
children: [{ id: 7, title: 'three`s great-grandchild', children: [] }],
},
],
},
],
},
]
/**
* @param items - [{..., children: ...}]
* @info children key is remove and parents is set instead
* @returns flatten array and remember parents in array
*/
const deepFlattenRememberParents = (items) => {
const flatten = JSON.parse(JSON.stringify(items)) // Important - create a deep copy of 'items' / preferably use lodash '_.cloneDeep(items)', but for the example this will do
for (let i = 0; i < flatten.length; i++) {
if (flatten[i].hasOwnProperty('children')) {
flatten[i].children.map((child) => {
if (flatten[i].hasOwnProperty('parents')) {
child.parents = [...flatten[i].parents, flatten[i]]
} else {
child.parents = [flatten[i]]
}
return child
})
flatten.splice(i + 1, 0, ...flatten[i].children)
delete flatten[i].children
}
if (!flatten[i].hasOwnProperty('parents')) {
flatten[i].parents = []
}
}
return flatten
}
/**
* @param items - [{..., children: ...}]
* @returns flatten array
*/
const deepFlatten = (items) => {
const flatten = JSON.parse(JSON.stringify(items)) // Important - create a deep copy of 'items' / preferably use lodash '_.cloneDeep(items)', but for the example this will do
for (let i = 0; i < flatten.length; i++) {
if (flatten[i].hasOwnProperty('children')) {
flatten.splice(i + 1, 0, ...flatten[i].children)
delete flatten[i].children
}
}
return flatten
}
console.log('deepFlattenRememberParents ', deepFlattenRememberParents(items))
console.log('deepFlatten ', deepFlatten(items))
Upvotes: 0
Reputation: 1445
For the example above, this should do.
const result = []
arr.map((obj) => {
if (obj.children) {
const el = {...obj, ...{}}
delete el.children
result.push(el)
Object.values(obj.children).map((v, i) => {
result.push(v)
})
} else {
result.push(obj)
}
})
console.log(result)
Upvotes: 2
Reputation: 2771
So there's Array.prototype.flat
, but that doesn't deal with lists of Objects where one key (how should it know, which) should be flattened.
But you can always resort to Array.prototype.reduce
to achieve that yourselves:
const SomeComponent = 'SomeComponent';
const arr = [
{path:'/foo', component: SomeComponent, children: [
{path:'/one', component: SomeComponent},
{path:'/two', component: SomeComponent},
{path:'/three', component: SomeComponent}
]},
{path: '/bar', component: SomeComponent}
];
function myFlat(a, prefix = '') {
return a.reduce(function (flattened, {path, component, children}) {
path = prefix + path;
return flattened
.concat([{path, component}])
.concat(children ? myFlat(children, path) : []);
}, []);
}
console.log(myFlat(arr));
Upvotes: 2
Reputation: 4770
You can try this
flattenArr = arr => {
const result = [];
arr.forEach(item => {
const {path, component, children} = item;
result.push({path, component});
if (children)
result.push(...flattenArr(children));
});
return result;
}
Upvotes: 0