I'm trying to manipulate an array like this:
data = [
{
"id":"1",
"items":[
{
"title":"item 1"
},
{
"title":"item 2"
}
]
},
{
"id":"2",
"items":[
{
"title":"item2 1"
},
{
"title":"item2 2"
}
]
}
]
I need, for example, to push another array:
[
{
"title":"item new 1"
},
{
"title":"item new 2"
}
]
inside data[0].items and obtain:
data = [
{
"id":"1",
"items":[
{
"title":"item new 1"
},
{
"title":"item new 2"
}
]
},
{
"id":"2",
"items":[
{
"title":"item2 1"
},
{
"title":"item2 2"
}
]
}
]
...how can I do this maintaining immutability, for example with Lodash? Not understand anding how to change only a specific sub object in a data structure. Somebody have suggestions?
Thanks
Presented below is one possible way to immutably add given array into a particular index "items" prop.
Code Snippet
const immutableAdd = (aIdx, addThis, orig) => {
const newData = structuredClone(orig);
newData[aIdx].items = addThis;
return newData;
};
const data = [{
"id": "1",
"items": [{
"title": "item 1"
},
{
"title": "item 2"
}
]
},
{
"id": "2",
"items": [{
"title": "item2 1"
},
{
"title": "item2 2"
}
]
}
];
const addThisArr = [{
"title": "item new 1"
},
{
"title": "item new 2"
}
];
console.log('immutableAdd result: ', immutableAdd(0, addThisArr, data));
console.log('original data: ', data);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
structuredClone() to deep-clone existing data arrayaIdx of the cloned-arrayaIdx's items propNOTE
This solution does not use lodash as it is not mandatory (to use lodash) to perform immutable operations.
If you want to maintain the immutability of original data, just map the content of original data to the new data as you want, and wrap your logic into a pure function to improve readability.
const dataOriginal = [{
"id": "1",
"items": [{
"title": "item 1"
},
{
"title": "item 2"
}
]
},
{
"id": "2",
"items": [{
"title": "item2 1"
},
{
"title": "item2 2"
}
]
}
]
const dataNew = createDataWithSomethingNew(dataOriginal, [{
"title": "item new 1"
},
{
"title": "item new 2"
}
])
function createDataWithSomethingNew(data, props) {
return data.map(function changeItemsOfId1ToProps(value) {
if (value.id === '1') {
return {
id: value.id,
items: props
}
} else {
return value
}
})
}
lodash has a method _.update can modify object with the correct path in string provided.
Another method _.cloneDeep can copy you object deeply. So that change in the pre-copied object will not affect the cloned object.
Finally use a deep freeze function to call Object.freeze recursively on the cloned object to make it immutable.
var data = [
{
"id":"1",
"items":[
{
"title":"item 1"
},
{
"title":"item 2"
}
]
},
{
"id":"2",
"items":[
{
"title":"item2 1"
},
{
"title":"item2 2"
}
]
}
]
var clonedData = _.cloneDeep(data) // copy the full object to avoid modify the source data
// update the data of that path '[0].items' in clonedData
_.update(clonedData, '[0].items', function(n) {
return [
{
"title":"item new 1"
},
{
"title":"item new 2"
}
]
})
// provide object immutability
const deepFreeze = (obj1) => {
_.keys(obj1).forEach((property) => {
if (
typeof obj1[property] === "object" &&
!Object.isFrozen(obj1[property])
)
deepFreeze(obj1[property])
});
Object.freeze(obj1)
};
deepFreeze(clonedData)
data[2] = {id: 3} // data will be changed
data[1].items[2] = {title: "3"} // and also this one
clonedData[2] = {id: 3} // nothing will be changed
clonedData[1].items[2] = {title: "3"} // and also this one
console.log(`data:`, data);
console.log(`clonedData:`, clonedData);