You can use a mix of .map and the ... spread operator
You can set the value after you've created your new array
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {return {...a}})
array2.find(a => a.id == 2).name = "Not Two";
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Or you can do it in the .map
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {
var returnValue = {...a};
if (a.id == 2) {
returnValue.name = "Not Two";
}
return returnValue
})
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Answer from George on Stack OverflowYou can use a mix of .map and the ... spread operator
You can set the value after you've created your new array
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {return {...a}})
array2.find(a => a.id == 2).name = "Not Two";
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Or you can do it in the .map
let array = [{id:1,name:'One'}, {id:2, name:'Two'}, {id:3, name: 'Three'}];
let array2 = array.map(a => {
var returnValue = {...a};
if (a.id == 2) {
returnValue.name = "Not Two";
}
return returnValue
})
console.log(array);
console.log(array2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Spred Operator, you can update particular array value using following method
let array = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const label = "name";
const newValue = "Two Updated";
// Errow comes if index was string, so make sure it was integer
const index = 1; // second element,
const updatedArray = [
...array.slice(0, index),
{
// here update data value
...array[index],
[label]: newValue,
},
...array.slice(index + 1),
];
console.log(updatedArray);
When you update item[key] inside the forEach, it just updates name and email with a string values. Also, it mutates the state
Instead, you could loop throguh editedData object update the clone of that specific index. Use the spread syntax to keep error and other properties as it is and update only the value property. Then update the index of the cloned data array and call setState like this:
const key = "02",
editedData = { name: "updated jaison 2", email: "[email protected]" },
data = [...this.state.data],
index = data.findIndex(item => key === item.id),
updatedData = { ...data[index] };
// loop through and update only the keys you need
for(const key in editedData) {
updatedData[key] = { ...updatedData[key], value: editedData[key] }
}
data[index] = updatedData;
this.setState({ data })
Instead of finding the index and using a forEach to loop over the array, I'd do something like:
const updatedArray = newData.map(item => {
// if editedData has a key attribute:
if (item.id === editedData.key) {
return editedData; //you'd need to add a key attribute to the data
} else {
return item;
}
});
this.setState({data: updatedArray});
The main reason I can see for this is to create a shallow copy of the object, so that if you perform any operations on the copy, it won't mutate the original. This would be especially useful in a functional programming paradigm where you don't want to mutate any shared objects/maintain a shared state.
However, as I mentioned, this only creates a shallow copy (as opposed to a deep copy), so that if any of the properties of the object are themselves mutable (such as an array property), then those properties would still be able to be mutated.
Edit: From the question in the comments, and building off of what @AdiH mentioned in their answer, it doesn't seem to make any sense to update post.title before creating the shallow copy as this would mutate the original object. It's possible that the author intended for this change to be reflected in both the original and the copy, but for any further changes to be reflected only in the copy
const x = { a: 'b', c: 'd', d: [] };
const copy = { ...x };
copy.a = 'c';
console.log(x); // unchanged
const same = x;
same.a = 'c';
console.log(x); // changed
copy.d.push('e');
console.log(x); // x.d has been changed
I can see your confusion. I think the example is not great. One can only guess what the author had in mind but if he or she intended to enforce immutability I would not have done post.title = "Updated" since it obviously mutates post.
Instead I would have done
post[index] = { ...post, title: "Updated"} see examples of JS Spread syntax.
So yes, I'm also unable to guess the motivation behind that example.
The properties are added in order, so if you want to override existing properties, you need to put them at the end instead of at the beginning:
return {
value: {
...initialState,
...newObject
}
}
You don't need newObject (unless you already have it lying around), though:
return {
value: {
...initialState,
isAvailable: newValue
}
}
Example:
const o1 = {a: "original a", b: "original b"};
// Doesn't work:
const o2 = {a: "updated a", ...o1};
console.log(o2);
// Works:
const o3 = {...o1, a: "updated a"};
console.log(o3);
If you know the name of the property (a in the example below), then @crowder's answer is perfect:
const o3 = {...o1, a: "updated a"};
console.log(o3);
If the property name is in a variable, then you need to use Computed Property names syntax:
let variable = 'foo'
const o4 = {...o1, [variable]: "updated foo"};
console.log(o4);
use Array.slice
this.setState({
images: [
...this.state.images.slice(0, 4),
updatedImage,
...this.state.images.slice(5),
],
});
Edit from original post: changed the 3 o a 4 in the second parameter of the slice method since the second parameter points to the member of the array that is beyond the last one kept, it now correctly answers the original question.
Once the change array by copy proposal is widely supported (it's at Stage 3, so should be finding its way into JavaScript engines), you'll be able to do this with the new with method:
// Using a Stage 3 proposal, not widely supported yet as of Nov 17 2022
this.setState({images: this.state.images.with(4, updatedImage)});
Until then, Object.assign does the job:
this.setState({images: Object.assign([], this.state.images, {4: updatedImage}));
...but involves a temporary object (the one at the end). Still, just the one temp object... If you do this with slice and spreading out arrays, it involve several more temporary objects (the two arrays from slice, the iterators for them, the result objects created by calling the iterator's next function [inside the ... handle], etc.).
It works because normal JS arrays aren't really arrays1 (this is subject to optimization, of course), they're objects with some special features. Their "indexes" are actually property names meeting certain criteria2. So there, we're spreading out this.state.images into a new array, passing that into Object.assign as the target, and giving Object.assign an object with a property named "4" (yes, it ends up being a string but we're allowed to write it as a number) with the value we want to update.
Live Example:
const a = [0, 1, 2, 3, 4, 5, 6, 7];
const b = Object.assign([], a, {4: "four"});
console.log(b);
If the 4 can be variable, that's fine, you can use a computed property name (new in ES2015):
let n = 4;
this.setState({images: Object.assign([], this.state.images, {[n]: updatedImage}));
Note the [] around n.
Live Example:
const a = [0, 1, 2, 3, 4, 5, 6, 7];
const index = 4;
const b = Object.assign([], a, {[index]: "four"});
console.log(b);
1 Disclosure: That's a post on my anemic little blog.
2 It's the second paragraph after the bullet list:
An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 253-1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 232-1.
So that Object.assign does the same thing as your create-the-array-then-update-index-4.