'Create an array of objects from array of objects which has nested fields JavaScript/es6
I am working on an array of objects which has nested attributes, Is there any way to write a recursive function to achieve below output mentioned
const firstArray = [
{
groupId: '1',
childRows: [
{
groupId: '1a',
childRows: ['abc', 'def'],
},
{
groupId: '1b',
childRows: ['pqr', 'xyz'],
},
],
},
{
groupId: '2',
childRows: [
{
groupId: '2a',
childRows: ['abz', 'dxy'],
},
{
groupId: '2b',
childRows: ['egh', 'mno'],
},
],
},
];
How to write a function in es6 such that below output is returned
[
{ groupId: '1', childRows: ['abc', 'def', 'pqr', 'xyz'] },
{ groupId: '1a', childRows: ['abc', 'def'] },
{ groupId: '1b', childRows: ['pqr', 'xyz'] },
{ groupId: '2', childRows: ['abz', 'dxy', 'egh', 'mno'] },
{ groupId: '2a', childRows: ['abz', 'dxy'] },
{ groupId: '2b', childRows: ['egh', 'mno'] },
];
Solution 1:[1]
I found a solution. I tried to explain you with comments in the code
const firstArray = [
{
groupId: '1',
childRows: [
{groupId: '1a',childRows: ['abc', 'def']},
{groupId: '1b',childRows: ['pqr', 'xyz']}
]
},
{
groupId: '2',
childRows: [
{groupId: '2a',childRows: ['abz', 'dxy']},
{ groupId: '2b',childRows: ['egh', 'mno']}
]
},
];
function solution (arr){
const result = []
for (let i = 0; i < arr.length ; i++) {
let parent = [] // this is where I will accumulate all the "childRows" of the same parent
let oldResultLengt = result.length // remember this for now
for (let j = 0; j < arr[i].childRows.length; j++) {
const _childRows = arr[i].childRows[j].childRows // save the specific child array
result.push({'groupId':arr[i].childRows[j].groupId, 'childRows': _childRows}) // put the object into result array
parent.push(..._childRows) // add to parent array
}
/* in this part of the code, at the first iteration the let result looks this:
[
{"groupId": "1a", "childRows": ["abc", "def"]},
{"groupId": "1b", "childRows": ["pqr", "xyz"]}
]
but oldResultLength is still 0, so I use splice to insert the parent element before their own childrens
*/
result.splice(oldResultLengt, 0, {'groupId' : arr[i].groupId, 'childRows': parent})
}
return result
}
console.log(solution(firstArray))
Function without comments:
function solution (arr){
const result = []
for (let i = 0; i < arr.length ; i++) {
let parent = []
let oldResultLengt = result.length
for (let j = 0; j < arr[i].childRows.length; j++) {
const _childRows = arr[i].childRows[j].childRows
result.push({'groupId':arr[i].childRows[j].groupId, 'childRows': _childRows})
parent.push(..._childRows)
}
result.splice(oldResultLengt, 0, {'groupId' : arr[i].groupId, 'childRows': parent})
}
return result
}
Solution 2:[2]
I came up with this recursive function. The output objects are in the intended order.
const getNestedGroups = (array, groups = []) => {
const finalArray = [];
// Group list should be empty as it is filled here.
groups.length = 0;
array.forEach((group) => {
if (group.childRows.length > 0) {
// If the group does not have nested groups we just append it to the group list.
if (typeof group.childRows[0] === "string") {
groups.push(group);
}
// If the group has children, the same function is called for them.
else {
// Call function for child
const directChildren = [];
const childGroups = getNestedGroups(group.childRows, directChildren);
// Makes an object from the direct children (which were also made from their direct children if they had some).
let groupWithChildren = { groupId: group.groupId, childRows: [] };
childGroups.forEach((child) => {
groupWithChildren.childRows.push(...child.childRows);
});
// Adds child to group list.
groups.push(groupWithChildren);
groups.push(...directChildren);
}
}
});
// Adds the new groups to the output array.
finalArray.push(...groups)
return finalArray;
}
You then call the function.
console.log(getNestedGroups(firstArray));
Here is the output.
[
{ groupId: '1', childRows: [ 'abc', 'def', 'pqr', 'xyz' ] },
{ groupId: '1a', childRows: [ 'abc', 'def' ] },
{ groupId: '1b', childRows: [ 'pqr', 'xyz' ] },
{ groupId: '2', childRows: [ 'abz', 'dxy', 'egh', 'mno' ] },
{ groupId: '2a', childRows: [ 'abz', 'dxy' ] },
{ groupId: '2b', childRows: [ 'egh', 'mno' ] }
]
Edit : Thanks to J. Villasmil, I saw we can optimize array1 = array1.concat(array2) with array1.push(...array2). I so updated my answer.
Solution 3:[3]
A nice way to say this is that the childRows prop contains either strings or objects, and the childRowsIn function returns the childRows's strings and the childRowsIn the childRows's objects
function childRowsIn(object) {
const childRows = (object.childRows || []).filter(e => ['string','object'].includes(typeof e));
return childRows.map(e =>
typeof e === 'string'? e: childRowsIn(e)
).flat()
}
const object = getData();
console.log(childRowsIn(object))
function getData() {
const firstArray = [{
groupId: '1',
childRows: [{
groupId: '1a',
childRows: ['abc', 'def'],
},
{
groupId: '1b',
childRows: ['pqr', 'xyz'],
},
],
},
{
groupId: '2',
childRows: [{
groupId: '2a',
childRows: ['abz', 'dxy'],
},
{
groupId: '2b',
childRows: ['egh', 'mno'],
},
],
},
];
return {
childRows: firstArray
};
}
Solution 4:[4]
Using flatMap, concat and destructuring can simplify as
const process = (arr) =>
arr.flatMap(({ groupId, childRows }) =>
childRows.concat({
groupId,
childRows: childRows.flatMap(({ childRows }) => childRows),
})
);
const firstArray = [
{
groupId: "1",
childRows: [
{
groupId: "1a",
childRows: ["abc", "def"],
},
{
groupId: "1b",
childRows: ["pqr", "xyz"],
},
],
},
{
groupId: "2",
childRows: [
{
groupId: "2a",
childRows: ["abz", "dxy"],
},
{
groupId: "2b",
childRows: ["egh", "mno"],
},
],
},
];
console.log(process(firstArray));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | |
| Solution 4 | Siva K V |
