1

I'm having a bit of trouble figuring out why the persons array contains the new age key:value pair.
I thought by slicing it (and therefore creating an independent copy) I could work solely on the newArray and only append age to it ... but it seems to be updating persons as well??
Thanks in advance

const persons = [
  { first: 'Albert', last: 'Einstein', birthYear: 1879, deathYear: 1955 },
  { first: 'Isaac', last: 'Newton', birthYear: 1643, deathYear: 1727 },
  { first: 'Galileo', last: 'Galilei', birthYear: 1564, deathYear: 1642 },
  ];

function appendAge(originalArray) {
  var newArray = originalArray.slice(0);

  newArray.forEach(item => {
    item['age'] = item.deathYear - item.birthYear;
  })
  return newArray
}
console.info(persons)
var newPersons = appendAge(persons)
console.info(newPersons)
Adam Azad
  • 11,171
  • 5
  • 29
  • 70
sigmazen
  • 223
  • 2
  • 4
  • 14

3 Answers3

3

slice does a shallow copy, meaning it only copies the top level of values, including references, and doesn't actually copy the referenced values themselves.

This means each person object in both of your arrays is the same object.

A simple solution would be to create a custom copy function for your person object and use that along with map to copy an array.

const people = [
  { first: 'Albert', last: 'Einstein', birthYear: 1879, deathYear: 1955 },
  { first: 'Isaac', last: 'Newton', birthYear: 1643, deathYear: 1727 },
  { first: 'Galileo', last: 'Galilei', birthYear: 1564, deathYear: 1642 },
  ];

function copyPerson(person) {
  return Object.assign({}, person);
}

function copyPeople(people) {
  return people.map(copyPerson);
}

function appendAge(people) {
  const newArray = copyPeople(people);

  newArray.forEach(item => {
    item['age'] = item.deathYear - item.birthYear;
  });

  return newArray;
}

console.info('----------------------------------------');
console.info('Old People');
console.log(people)
console.info('----------------------------------------');
console.info('New People');
console.log(appendAge(people));

You can also check out How do I correctly clone an object in JavaScript for other solutions.

Community
  • 1
  • 1
nem035
  • 34,790
  • 6
  • 87
  • 99
  • There's no need to use `.slice()` if you also call `.map()`, since `.map()` creates a new array. – Barmar Jan 06 '17 at 21:08
  • awesome ... thanks for the super-quick reply ... I'd read a bit about shallow vs deep copy but hadn't really got to grips with it ... this solution is perfect. – sigmazen Jan 06 '17 at 21:20
  • @sigmazen glad to help out :) – nem035 Jan 06 '17 at 21:27
3

You've created a copy of the persons array, that contains references to the original objects, and not a copy of those objects. To create a full copy, you'll need to clone the objects as well.

Note that Object#assign will only copy the top level properties of the object, so you'll need to deep clone the objects instead.

const persons = [
  { first: 'Albert', last: 'Einstein', birthYear: 1879, deathYear: 1955 },
  { first: 'Isaac', last: 'Newton', birthYear: 1643, deathYear: 1727 },
  { first: 'Galileo', last: 'Galilei', birthYear: 1564, deathYear: 1642 },
  ];

function appendAge(originalArray) {
  return originalArray.map((item) => Object.assign({}, item, {
    age: item.deathYear - item.birthYear
  }));
}

var newPersons = appendAge(persons);
console.info(newPersons);
console.info(persons); // persons haven't changed
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

Your .forEach loop is actually what's updating the Array, not the slice. The slice is making a copy but when you pass it comes into the forEach loop. So when you're adding your items to what you think is the copy, you're adding to the original.

Dylan Wright
  • 1,118
  • 12
  • 19
  • I think this answers your question. I guess I don't see why you'd update a copy. Maybe you're doing a comparison? If so, do a deep clone of the original, store it into a variable at init, then at save/update compare orig to updated... just guessing here – Dylan Wright Jan 06 '17 at 21:19