Copy array of Objects in angular 2 - javascript

I have two array called 'persons' and 'persons2',
The 'persons2' array would to be copy of 'persons' array,
But the problem is when I copy it, and I want to change the second array, the first array is also changing. This is my code:
export class AppComponent {
persons = [
{
name:'David',
lname:'Jeu'
}
];
persons2=[...this.persons];
constructor(){
console.log(this.persons[0]);
this.persons2[0].name='Drake';
console.log(this.persons[0]);
console.log(this.persons2[0]);
}
}

But the problem is when I copy it, and I want to change the second
array, the first array is also changing
That is because the objects inside both the arrays are sharing same reference. To perform a deep copy try the following :
let persons2 = person.map(x => Object.assign({}, x));
Or
let person2 = JSON.parse(JSON.stringify(person));

In your case both the array is referring to the same memory, which is commonly known as shallow copy.
You can make a deep copy of the first array and then change the second array. That will have no impact on the first array.
let persons = [{
name: 'David',
lname: 'Jeu'
}];
let persons2 = JSON.parse(JSON.stringify(persons));
persons2[0].age = 29;
console.log(persons)
console.log(persons2)

For these kinds of operations it is usually wise using Lodash Clonedeep

Related

Array of objects, remove the array after push, keep objects

Im trying to push my array of objects into variable, but all i I recieve is Array in Array, or single object.
myObject = {
id: id,
items: [],
boolean: true,
}
myArray = [{1}, {2}, {3}]
I tried myObject.items.push(myArray[0]) but this returns only first object. Without 0 its double array.
What i want is
myObject = {
id: id,
items: [{1}, {2}, {3}],
boolean: true,
}
What you're going to want to do here is set the entire array as the new value like this:
myObject.items = myArray
If you want to take the immutable approach then you can copy it like this:
myObject.items = [...myArray]
Edit:
If you want to add items (and not just complete overwrite) to myObject.items then you should do this:
myObject.items = [...myObject.items, ...myArray]
That will add your new array items to the end of the current items array, you could also do this to add them to the start:
myObject.items = [...myArray, ...myObject.items]
you can use this
myObject.items.push(...myArray)
As Andreas commented another solution is to use concat. Concat is similar to push. But it doesn't actually change the array it returns a new one.
const x = []
console.log(x.concat(3)) // prints [3]
console.log(x) // prints []
This behavior is often desired as it prevents "side effects" from occurring
It also doesn't just append items to an array, it only does this if the item is not an array. If it is an array it will merge the two arrays
const x = [1,2,3]
console.log(x.concat([4,5,6]) // prints [1,2,3,4,5,6]
so a solution here is
myObject.items = myObject.items.concat(myArray)
//object
myObject={
id:1,
items:[],
boolean: true
}
//concat
myArray = [ 1,2,3];
myObject.items += myArray;
console.log(myObject.items);

Why does the copy of an array using the spread operator when run through map modify the original array?

Why does the copy of an array using the spread operator when run through map modify the original array?
What should i do here to not mutate the original array?
const data = {hello : "10"};
const prop = [{name : "hello", color: "red", value : ""}]
const copyProp = [ ...prop ]
copyProp.map(el => {
el.value = data[el.name] || ""
return el
}) //
console.log(copyProp === prop) // -> false
console.log(copyProp) // -> value: 10
console.log(prop) // -> Value: 10 (Should still be "")
The spread operator creates shallow copy of the array. In other words, you create a new array with references to the same objects. So when you modify those objects, the changes are reflected in the original array.
In general, when you need copy an array, you should consider making a deep copy. However, in this case, you just need to use map() correctly. map() creates a new array, so it can make the modified copy for you directly:
const copyProps = props.map(el => {
return {
...el,
value: data[el.name] || '',
}
});
Here I copy each object using the spread operator. This means the resulting array has its own references of objects. This has the same caveat as your original solution: this is a shallow copy. For your example data, it is fine because all values and keys are strings. However, if your real data is more deeply nested with more arrays and objects, you will encounter the same problem.
Both arrays and objects are passed by reference, so when you spread an array you create new array but fill it with references to original objects and when you modify those objects in the new array you still modify the same data in memory that is referenced in both arrays.
Also map method will always return new array so in this case you only need to clone objects and since here you do not have deeply nested objects you can use object spread syntax.
const data = {
hello: "10"
};
const prop = [{
name: "hello",
color: "red",
value: ""
}]
const copyProp = prop.map(el => ({ ...el,
value: data[el.name] || ''
}))
console.log(copyProp)
console.log(prop)

Is it possible for map to mutate object?

I have a very strange case which indicates that either map function mutates object or lodash incorrectly clones the object. This piece of code is just a sample: a function that takes an object as an argument and returns a copy of it using cloneDeep from lodash. Then I use this object to generate a chart.
const copyObject = data => {
const copy = _.cloneDeep(data);
const dataWithIndexes = copy.nodes.map((node, index) => {
node.index = index;
return node;
});
return copy;
};
export const copiedData = copyObject(sampleData);
Entry data in this case is an object with arrays of objects:
{
nodes: [
{ name: "transactions.main", layer: 0 },
...
],
links: [
{ source: 3, target: 3, value: 4 },
...
]
}
As you can see map inside function is not used at all and this is the point. When I am using original, unmodified object within my chart generate function it works fine, when I am copying the object with function shown above, it doesn't work, BUT when I comment this dataWithIndexes variable it starts working again. Is it possible in any way that map mutates the copied object? Or maybe it's lodash's fault? It may clone the object incorrectly, but on the other hand I only use the map on it, it doesn't modify it in any way.
Maybe anyone can help me solving this riddle T_T
Thanks
You are modifying the node object parameter in the map(...) callback by overwriting its index property: node.index = index. This way the original object in the array is getting mutated although it returns a new array.
This would still happen even though you are not using the dataWithIndexes that is because the map(...) is still run and when it does it mutates the objects in the copy.node array with the new values of the index in the callback.
To avoid it, make a copy of the node object parameter in the map call and assign the new index there and then return it from the callback:
const dataWithIndexes = copy.nodes.map((node, index) => {
return {...node, index};
});

can I use forEach to make every element of an array a new object?

I created an array with many elements with a loop:
myArray = [c1, c2, c3...]
now I want to make each element into an object and assign different key values:
c1 = {image = path, value = number)
I tried to run forEach() but can't figure out the correct way to do so and I have not succeeded in finding the answer to it.
My guess was:
myArray.forEach(function() {
let name = {
image = path,
value = number,
}
return name;
});
but there's no change in the elements in the log.
Any help or link to an answer that can help me here. First time coding here.
UPDATE: an easier solution was to .push all the keys and values of the objects when I created the array with the loop in the first place.
array.push({image: pathx, value: numberx})
You can, but you'd be better off with a simple for loop:
for (let i = 0; i < myArray.length; ++i) {
let entry = myArray[i];
myArray[i] = {image: entry.path, value: entry.number};
}
Or making a new array with map.
newArray = myArray.map(entry => ({image: entry.path, value: entry.number}));
Or if you prefer non-arrow functions:
newArray = myArray.map(function(entry) {
return {image: entry.path, value: entry.number};
});
You could theoretically push to a new array but this is the exact usecase for Array#map. Array#map maps old values to new values. The returned object from the callback is the new object and the returned array is the new array containing the new objects.
Semantically, Array#forEach is to plainly iterate over each element in an array, and possibly execute something with side-effects (which may include pushing to another array). But with Array#map, it's specifically used to transform old array values to new ones. Use the one that is specifically designed because it conveys a clear message to anyone else who reads your code.
const newArray = myArray.map(({ path, number }) => ({
image: path,
value: number
}));
Array#map maps old values to new values. You may need to use the follwing instead of arrow functions as it is not supported in IE.
I just added dummy data in the object, you can change it.
myArray = ["c1", "c2", "c3"]
myArray = myArray.map(function(elem) {
return {"image":"path"+elem,"value":"value"+elem};
});
console.log(myArray);

How to mutate original array in Javascript .map() function?

For eg:
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];
And you want to change the value of "salary" of each person in an original array.
If you want to mutate the original array, you can use Array#forEach function.
const persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary": 1500 }];
persons.forEach(item => item.salary += 1000);
console.log(persons)
Array#map creates a new array of the created items and returns that. After you need to assign the returned result.
let persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary": 1500 }];
persons = persons.map(item => {
item.salary += 1000;
return item;
});
console.log(persons);
You can mutate the objects directly iterating with map. If I understood you correctly.
persons.map(i => { i.salary = i.salary * 1.25; return i; });
console.log(persons);
// [{ "name":"A", "salary": 1875 }, { "name":"B", "salary": 2343.75 }]
Though it's anti-pattern and you should avoid performing mutations of your original array in map().
Use forEach. If your array is full of objects, and you simply want to mutate those objects — like in the original question — then you can simply have your callback function mutate the item passed to it in its first argument.
If your array is full of primitives, which can only be changed by assigning something else to the array slots, thereby mutating the array — or if for some other reason you want to reassign things to the array slots — you can still use forEach. The second parameter and third parameters passed to the callback are the array index of the element currently being operated on, and a reference to the array itself. So within the callback function, you can assign to the relevant array slot.
you can use a simple for loop for that
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];
for(let element of persons){
element.salary*=2;
}
console.log(persons);
.map() function takes third parameter in its callback thats the instance of original array.
You could do something like this also:
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":1500 }];
persons.map(function(person, key, array) {
array[key].salary *= 2;
});
console.log(persons);
I read other answers, you can use any of them, but I see some problems there.
I will mention 2 methodologies I have used in many different languages, map and forEach. map is a functional way of traversing a collection and creating some new collection with new (different or same) elements, independent of languages. With map, it is expected to create a new collection that is created by some mapping from initial collection. On the other hand, forEach is a method that eases traversing a collection by not using usual for loop syntax for collections, and mutating (or changing) each item if desired.
If you use map on a collection that contains objects, and change those objects in the mapper function, you might face with unexpected behavior. Beacuse you are changing directly the object you are operating on, and do not mapping it to another object. This object might can be considered as a state and computers works based on the state transfers. If you want to change that object, i.e. some state, it is absolutely ok, but based on the description, you should not use map for such a case. Because you are not creating a new array with some new values, but instead, mutating provided elements. Use forEach for such a case.
I have added an example here. You can click the link and take a look at the console, and see my what I mean in a more clear way.
As far as I know, based on my experience, mutations in map method is considered as bad practice and discouraged.
These two are added for different purposes and it would be better to use them as expected.
For more, see Mozilla Web Docs page for Array.
JavaScript has an inbuilt Array method map that iterate the values of an Array
persons.map(person => person["salary"] + 1000)
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];
var mutatedPersons = persons.map(function(obj){
return {name:obj.name,salary:parseInt(obj.salary) + 100};
})
console.log(mutatedPersons);
try:
let persons = persons.map((person) => {person['salary'] = parseInt(person['salary']) + 1; return person})
If you have an array of primitives, you can use this function:
function mapInplace<T>(arr: T[], callback: (v: T, i: number) => T) {
for(const [i, v] of arr.entries()) {
arr[i] = callback(v, i)
}
}
Example usage:
mapInplace(weights, w => w / total)
There's no return value since it's mutating the array.

Categories