I have a state:
const obj = [{id: "1", obj_of_names: [{
id: "a1",
name: "abc",
school: "xyz" },
{ id:"b1",
name: "def",
school: "zyx" },
{ id:"c1",
name: "ghi",
school: "pqr" }]
},
{id: "2", obj_of_names: [{
id: "d1",
name: "abc123",
school: "xyz123" }]
}]
const [newobj, setObj] = useState("obj")
Now I want to update school for the id:d1 inside id:2. Suppose the old value is school: xyz123 now I want to update it to school: newxyz123. How can I achieve this using the spread operator or by another method?
I tried the below code:
setObj((prevObjects) => {
const tempIndex = prevObjects.findIndex(
(item) => item.id === reqdImgID
);
const newObject = obj[tempIndex].obj_of_names.map((obj) => {
if (obj.id == reqdID) {
return { ...obj,
school: newSchool,};}
return obj; }););
But I am getting this as an ouput: { id:"c1", name: "ghi", school: "newxyz123" }
CodePudding user response:
I suggest creating a function that receives the original array, a matcher function, and a replace function. The purpose of this function is to return a new array with the value changed.
function mutateObject(array, matcher, replacer) {
// returns a new array, with all elements untouched, except for those that match the matcher function, which are replaced with new objects created by the replacer function
return array.map(function (item) {
return matcher(item) ? {...replacer(item)} : item;
});
}
Then, use the function to replace the values.
mutateObject(obj, group => group.id == "2", group => {
const newNames = mutateObject(group.obj_of_names, name => name.id === "d1", name => ({ ...name, school: "newxyz123" }));
return { ...group, obj_of_names: newNames }
});
What this will do:
- Using the original array, find the object where the id (in the root object) is "2".
- Apply the replacer function, which will:
- Use the
obj_of_names
to find the object where the id is "d1". - Apply the replacer function to return a new object with the new school value, using the spread operator.
- Return a new object cloned by the spread operator with replaced
obj_of_names
object, which contains the replaced school value.
- Use the
Of course, you can improve this code and its readability by using various libraries to clone the objects, mutate them, and return new objects, but it is a workaround if you want to get started. Also, if you want to change the school name, I suggest using the answer @aweimon referenced, but if you want to change multiple objects on various criteria, you can improve the function above.
const obj = [{
id: "1",
obj_of_names: [{
id: "a1",
name: "abc",
school: "xyz"
},
{
id: "b1",
name: "def",
school: "zyx"
},
{
id: "c1",
name: "ghi",
school: "pqr"
}
]
},
{
id: "2",
obj_of_names: [{
id: "d1",
name: "abc123",
school: "xyz123"
}]
}
]
function mutateObject(array, matcher, replacer) {
// returns a new array, with all elements untouched, except for those that match the matcher function, which are replaced with new objects created by the replacer function
return array.map(function(item) {
return matcher(item) ? { ...replacer(item)
} : item;
});
}
const updatedObj = mutateObject(obj, group => group.id == "2", group => {
const newNames = mutateObject(group.obj_of_names, name => name.id === "d1", name => ({ ...name,
school: "newxyz123"
}));
return { ...group,
obj_of_names: newNames
}
});
console.log(updatedObj);
CodePudding user response:
const arr = [{id: "1", obj_of_names: [{
id: "a1",
name: "abc",
school: "xyz" },
{ id:"b1",
name: "def",
school: "zyx" },
{ id:"c1",
name: "ghi",
school: "pqr" }]
},
{id: "2", obj_of_names: [{
id: "d1",
name: "abc123",
school: "xyz123" }]
}]
You can call a map inside another map and change the array whitin the object like so:
const newArr = arr.map((item)=>{
if(item.id == 2){
return {...item, obj_of_names: item.obj_of_names.map((item2)=>{
if(item2.id === 'd1'){
return {...item2, school: 'newxyz123'}
}
return item2
}) }
}
return item
})
const [state, setState] = useState(arr)
Then you can call setState
and update it with the newArr