0

I'm trying to update a nested array immutably, but I get referential equality between the two results.

I'm expecting updated.a !== obj.a but I'm seeing in the console that they are equal.

function update(dest, source) {
  return Object.entries(source).reduce((acc, [key, val]) => {
    if (Array.isArray(val)) {
      acc[key] = [...val];
    } else if (isPlainObject(val)) {
      acc[key] = { ...update(dest[key], source[key])
      };
    } else {
      acc[key] = val;
    }

    return acc;
  }, dest);
}

function isPlainObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}

const obj = {
  a: [1, 2],
};

const updated = update(obj, {
  a: [1, 2]
});

console.log(updated.a === obj.a);
Barmar
  • 741,623
  • 53
  • 500
  • 612
Andy Jessop
  • 225
  • 1
  • 13
  • 2
    You need to deep copy `dest` when passing it to `reduce`. Immutable operations always require some sort of copying. – kelsny Aug 31 '22 at 19:22
  • The problem is that you're updating `dest` in place, so `updated == obj`. – Barmar Aug 31 '22 at 19:29
  • also `Object.prototype.toString.call(obj)` isn't a very robust type check, see: [Check if a value is an object in JavaScript](https://stackoverflow.com/questions/8511281/check-if-a-value-is-an-object-in-javascript) – pilchard Aug 31 '22 at 21:00

1 Answers1

0

Just create a new copy of dest object using Object.create

function update(dest, source) {
  return Object.entries(source).reduce((acc, [key, val]) => {
    if (Array.isArray(val)) {
      acc[key] = [...val];
    } else if (isPlainObject(val)) {
      acc[key] = { ...update(dest[key], source[key]) };
    } else {
      acc[key] = val;
    }

    return acc;
  }, Object.create(dest));
}

function isPlainObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}

const obj = {
  a: [1, 2],
};

const updated = update(obj, { a: [1, 2] });

console.log(updated.a === obj.a);
minhazmiraz
  • 442
  • 4
  • 12