'Find and clone and add the clone after the found values with javascript in a object of arrays
I am trying to find a match in a object of arrays and clone this, change the ID and insert this after the found match.
Each plan has clusters and each cluster has goals, the idea is that I need to clone a goal and insert this AFTER the cloned goal (it will be loaded below this goal in the UI).
Main structure
{
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
{
"id": 3,
"title": "Goal C",
},
],
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
},
],
},
]
}
My test code
// this would not work ofcourse!
const newId = 12345;
const matchToId = 2;
plan.clusters?.map(cluster => {
cluster?.goals?.map((goal, i) => {
if (goal.id === matchToId) {
// will copy goal with id 2
const copyGoal = goal;
return {...goal, ...copyGoal};
}
return {...goal};
});
// this will work but it will change the id but not copy and add a the new object
plan.clusters = clusters.map(cluster => {
return {
...cluster,
goals: cluster.goals?.filter(goal => {
if (itemId == goal.id) {
const cloned = goal;
cloned.id = 12345;
return {...goal, cloned};
}
return goal;
}),
};
});
What I want
{
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
// this will be added
{
"id": 12345,
"title": "COPIED GOAL",
},
// ---
{
"id": 3,
"title": "Goal C",
},
],
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
},
],
},
]
}
Solution 1:[1]
This may be one possible solution to achieve the desired objective.
Code Snippet
// check if "tgtId" is present in "goals" array (parameter: "arr")
// and if so, insert "newObj" (parameter: "obj") into the array
const copyObjToGoal = (arr, tgtId, obj) => {
let resObj = {goals: arr}; // the default "result object"
const tgtIdx = arr.findIndex( // search for "tgtId"
({ id }) => id === tgtId
);
if (~tgtIdx) { // if tgtIdx is not -1 (ie, tgtId was found in arr)
const newArr = [ // non-shallow-copy of "arr" (to prevent mutation)
...arr.map(
x => ({...x})
)
]; // mutate the copied "newArr", not the parameter "arr"
newArr.splice(
tgtIdx + 1, // add "after" the "tgtId" position in "goals" array
0, // 0 indicates not to remove any element
{...obj} // destructure to shallow-copy the "newObj" object
);
resObj = { // update the resObj by re-assigning
goals: [...newArr]
};
};
return resObj; // return the result-object
};
// search and insert new/copied goal (not mutating 'myObj')
const searchAndInsert = (tgtId, newObj, obj) => (
Object.fromEntries( // transform below result back into object
Object.entries(obj) // iterate key-value pairs of "obj"
.map(([k, v]) => {
if (k !== 'clusters') { // if key is not "clusters", no change
return [k, v];
} else { // else (for "clusters"
return [ // transform the "goals" array for each cluster
k, // where "tgtId" is found
v.map( // iterate over array of "clusters"
({goals, ...r1}, idx) => {
return { // return each "clusters" array object
...r1,
...copyObjToGoal(goals, tgtId, newObj)
}
}
)
];
}
})
)
);
const myObj = {
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
{
"id": 3,
"title": "Goal C",
}
]
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
}
]
}
]
};
const targetGoalId = 2;
const newGoalObj = { id: '12345', title: 'copied goal' };
console.log(
'Test case 1, from question\n',
searchAndInsert(targetGoalId, newGoalObj, myObj)
);
console.log(
'Test case 2, bespoke\n',
searchAndInsert(6, { id: '12345', title: 'copied again' }, myObj)
);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Explanation
Inline comments in the above snippet describe the significant aspects of the solution.
Notes
This solution employs below JavaScript features:
- Array
.findIndex() ...spread- Array
.splice() - Array
.map() Object.fromEntries()Object.entries()- De-structuring
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 | jsN00b |
