I read the official Vuex example of how to mutate the state of element in array of objects data.
editTodo (state, { todo, text = todo.text, done = todo.done }) {
const index = state.todos.indexOf(todo)
state.todos.splice(index, 1, {
...todo,
text,
done
})
}
It seems a bit an overkill to find the index first (first loop), perform a splice and perform a copy with spread operator ...todo (would it be considered a second loop?).
Going by the docs:
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or Replace that Object with a fresh one. For example, using the object spread syntax: state.obj = { ...state.obj, newProp: 123 }
There are only two ways to set / update properties to object.
In bigger data scenario that is causing some performance issues. Is there any better (more efficient) way to update an element in array of objects?
I have created a copy of a variable using Object.assign and I am able to change an array within the copy of the object fine without affecting the original object. However, if I try and update a value within an array on the copied object this is also affecting the original object (i.e. the changes are made to both "job" and "updatedJob").
I have the following code:
// Create copy of "job" object which has been passed through
let updatedJob = Object.assign({}, job);
// Remove images from updated job object
updatedJob.Images = null;
// Remove checklist images from updated job object
_.each(updatedJob.Checklists, function (checklist) { checklist.Image = null; });
As you can see I have created a copy of the "job" object that has been passed through.
If I then change the "Images" array of the "updateJob" object (to null) this works fine and does not affect the original "job" object (i.e. the original "job" object still has images intact).
However in the last line of the code I am trying to iterate the "Checklists" array witin the "updatedJob" object and change just the "Image" property to "null" for each one but doing this also updates the original "job" object.
How is this possible when the "updatedJob" object should be a copy of the original "job" object therefore any changes I make to it should not affect the original "job" object?
As stated in comments and other answers, you're doing a shallow copy. Does that _.each() in your code indicate that you're using lodash?
If so, use the deepClone() method so that items are copied recursively:
let updatedJob = _.cloneDeep(job)
Otherwise, there are other questions on SO that deal with this topic that will provide a suitable solution.
Object.assign does a shallow copy: It copies all the key-value pairs of one object into another, just like this:
const from = { primitive: "string", complex: { } }, to = {};
// Object.assign(from, to) basically does
to.primitive = from.primitive; // primitives are copied
to.complex = from.complex; // complex values references get copied, not the value itself
However, objects (arrays are objects too) are copied by reference, so in your case, both objects' Checklists properties point to the same array.
You could create a new array by using .map and reassign the property to point to the new array:
updatedJob.Checklists = updatedJob.Checklists.map(checklist => ({ ...checklist, Image: null }));
The reference thing here applies here again too, setting .Image to null would affect the object referenced in both arrays, therefore we have to copy these objects too.
Array.prototype.sort() method changes the array, so the value of the variable that refers to it is changed as well. It looks horrible to me, and I am unable to access the original array afterwards.
For example:
function keepOriginalArray([...arr]){
let storeWithoutSorting = arr; // I want original copy of array in variable.
arr.sort()
return storeWithoutSorting // gives result ['a','b'] . Variable's value was changed after calling sort method.
}
keepOriginalArray(['b', 'a'])
I am curious to know how does it work and how can I store a copy of the original array in a variable in this situation?
Sort mutates original array so you need to create a copy of array. here i am using ... spread syntax to create a shallow copy of original array
function keepOriginalArray(arr){
let storeWithoutSorting = [...arr];
arr.sort()
return storeWithoutSorting
}
console.log(keepOriginalArray(['b', 'a']))
Array is reference type. When you are storing arr value to storeWithoutSorting , you are actually storing its reference. Thats why both are getting sorted when you are performing the sort.
To create a copy of an array, you can use spread operator:
let storeWithoutSorting = [...arr]
It will create a new array with new reference. Hence it won't be manipulated when you perform sort on arr.
As I remember in past I could not update react-state manualy.
Now, I don't know why, I can do it.
My case:
and the property is changed here!It works! Why?
P.s: I use react16.02
the state does mutate in react. and u should be very careful that u don't override it like that or you will encounter unexpected result. it is always best to copy your state into another object, then change its properties as u like and after you are done use set state to set set your object state as copied object.
you can use Object.Assign or spread operator for this. spread operator is easier when you are using Arrays.
this.state = {
myObj : {propA: "A" , propB: "B"};
}
then at some point you want to change it
let myObj = this.state.myObj;
let copyMyObj = {...myObj};
copyMyObj.propA = "C";
this.setState({
myObj: copyMyObj
});
In javascript objects are mutable.
For example if
var a = {"something": 1}
and if you assign it to another variable b and if you change its value, the value of object a is also changed
var b = a;
b.something = 2;
console.log(b);
console.log(a);
//output {"something": 2}
both will print {"something": 2}. The value in a is also changed.
Similarly in your code you are directly assigning the state value in line number 61 and mutating it in line number 62.
So better way is to use
let newOptions = {...this.state.newOptions };
or
let newOptions = Object.assign({},this.state.newOptions}
This will create new object.
React state is mutable, so you can update react state directly. But it is recommended not to mutate react state due to multiple reasons. One of the reasons is, if you do this.state.count = 3, react DOM will behave in an unpredictable manner such as overwriting data and not re-rendering. Performance tuning is another reason.
React on calling setState() will call the render method in some near future which will cause the DOM to be updated with new values.
I've read many answers here relating to 'by value' and 'by reference' passing for sending arrays to javascript functions. I am however having a problem sending an array to a function and leaving the original array unaltered. This example llustrates the problem:
function myFunction(someArray)
{
// any function that makes an array based on a passed array;
// someArray has two dimensions;
// I've tried copying the passed array to a new array like this (I've also used 'someArray' directly in the code);
funcArray = new Array();
funcArray = someArray;
var i = 0;
for(i=0; i<funcArray.length; i++)
{
funcArray[i].reverse;
}
return funcArray;
}
I can't understand why anything in this function should alter the original array.
calling this function directly changes the original array if the function call is assigned to a new array:
myArray = [["A","B","C"],["D","E","F"],["G","H","I"]];
anotherArray = new Array();
anotherArray = myFunction(myArray);
// myArray gets modified!;
I tried using .valueOf() to send the primitive:
anotherArray = myFunction(myArray.valueOf());
// myArray gets modified!;
I have even tried breaking the array down element by element and sub-element by sub-element and assigning all to a new 2-d array and the original array still gets modified.
I have also joined the sub-elements to a string, processed them, split them back into arrays and the original array still gets modified.
Please, does any one know how I can pass the array values to a function and not have the passed array change?
Inside your function there's this:
funcArray = new Array();
funcArray = someArray;
This won't actually copy someArray but instead reference it, which is why the original array is modified.
You can use Array.slice() to create a so-called shallow copy of the array.
var funcArray = someArray.slice(0);
Modern versions of ES also support destructuring expressions, which make it look like this:
const funcArray = [...someArray];
The original array will be unaltered, but each of its elements would still reference their corresponding entries in the original array. For "deep cloning" you need to do this recursively; the most efficient way is discussed in the following question:
What is the most efficient way to deep clone an object in JavaScript?
Btw, I've added var before funcArray. Doing so makes it local to the function instead of being a global variable.
Make a copy of the array that you can use.
A simple way to do this is by using var clone = original.slice(0);
With ES6, you can use the rest element syntax (...) within a destructuring expression to perform a shallow copy directly in the parameter list, allowing you to keep the original array unaltered.
See example below:
const arr = [1, 2, 3, 4, 5];
function timesTen([...arr]) { // [...arr] shallow copies the array
for(let i = 0; i < arr.length; i++) {
arr[i] *= 10; // this would usually change the `arr` reference (but in our case doesn't)
}
return arr;
}
console.log(timesTen(arr));
console.log(arr); // unaltered
The above is useful if you have an array of primitives (strings, booleans, numbers, null, undefined, big ints, or symbols) because it does a shallow copy. If you have an array of objects, or an array of arrays (which are also technically just objects), you will want to perform a deep clone to avoid modifying the array and its references. The way to do that in modern-day JS is to use a structured clone, which helps deal with many of the shortcomings of deep cloning with the JSON.stringify() technique as previously used:
function myFunction(someArray) {
const funcArray = structuredClone(someArray);
...
}
What about destructuring assignment (ES6+, check compatibility)? Nice and clean solution.
function myFunction(someArray) {
for(let i = 0; i < someArray.length; i++)
{
someArray[i].reverse();
}
return someArray;
}
let myArray = [["A","B","C"],["D","E","F"],["G","H","I"]];
// Using destructuring assignment.
// NOTE: We can't just use `[...myArray]` because nested arrays will still be copied by reference.
let anotherArray = myFunction([...myArray.map(nested => [...nested])]);
console.log({original: myArray, copy: anotherArray});
A variable pointing to an array is a reference to it. When you pass an array, you're copying this reference.
You can make a shallow copy with slice(). If you want a full depth copy, then recurse in sub objects, keeping in mind the caveats when copying some objects.
If you need to do this with an object, try this fancy trick...
MY_NEW_OBJECT = JSON.parse(JSON.stringify(MY_OBJECT));
A generic solution would be...
// Use the JSON parse to clone the data.
function cloneData(data) {
// Convert the data into a string first
var jsonString = JSON.stringify(data);
// Parse the string to create a new instance of the data
return JSON.parse(jsonString);
}
// An array with data
var original = [1, 2, 3, 4];
function mutate(data) {
// This function changes a value in the array
data[2] = 4;
}
// Mutate clone
mutate(cloneData(original));
// Mutate original
mutate(original);
This works for objects as well as arrays.
Very effective when you need deep cloning or you don't know what the type is.
Deep cloning example...
var arrayWithObjects = [ { id: 1 }, { id: 2 }, { id: 3 } ];
function mutate(data) {
// In this case a property of an object is changed!
data[1].id = 4;
}
// Mutates a (DEEP) cloned version of the array
mutate(cloneData(arrayWithObjects));
console.log(arrayWithObjects[1].id) // ==> 2
Warnings
Using the JSON parser to clone is not the most performant option!
It doesn't clone functions only JSON supported data types
Cannot clone circular references
by default in javascript except objects and arrays, everything is copy-by-value
but if you want to use copy-by-value for arrays: use [yourArray].slice(0)
and for objects use Object.assign(target, ...sources)
var aArray = [0.0, 1.0, 2.0];
var aArrayCopy = aArray.concat();
aArrayCopy[0] = "A changed value.";
console.log("aArray: "+aArray[0]+", "+aArray[1]+", "+aArray[2]);
console.log("aArrayCopy: "+aArrayCopy[0]+", "+aArrayCopy[1]+", "+aArrayCopy[2]);
This answer has been edited. Initially I put forth that the new operator handled the solution, but soon afterward recognized that error. Instead, I opted to use the concat() method to create a copy. The original answer did not show the entire array, so the error was inadvertently concealed. The new output shown below will prove that this answer works as expected.
aArray: 0, 1, 2
aArrayCopy: A changed value., 1, 2