1

I would like to merge an array with another array. The only catch is that each array is within an object.

Intuitively I tried {...arrObj, ...newArrObj} however this leads newArrObj overwriting items in the arrObj.

const array = ['an', 'array'];
const newArray = [, , 'new', 'ehrray'];

const obj = {
  key: { ...array
  }
};

const newObj = {
  key: { ...newArray
  }
};

const merged = { ...obj,
  ...newObj
};

console.log(merged);

I would expect merged to be:

{
  "key": {
    "0": "an",
    "1": "array",
    "2": "new",
    "3": "ehrray"
  }
}

but receive

{
  "key": {
    "2": "new",
    "3": "ehrray"
  }
}
Logan C
  • 41
  • 3
  • The merge logic you want to use is not clear. Why *wouldn't* the `null` override the values of `array`? What do you expect to happen to them? Disappear? What would you expect to have happen when there are actual strings instead of `null` in the first two values? – obe Mar 06 '21 at 21:28
  • `merged = { key: { ...newArray, ...array } };` works for your specific example, but the task itself feels weird; maybe an XY problem? What's the purpose/context of this? What are you trying to achieve? – Thomas Mar 06 '21 at 21:31
  • The idea is that there are unset values with only a few set. eg. `const newArray = new Array(4); newArray[2] = 'new';` – Logan C Mar 06 '21 at 21:32
  • "unset" means that no value is set. In the example the `newArray` indexes `0` and `1` are both set to `null`. For an array with 2 unset indexes use `[ , , 'new', 'ehrray']` which is an array of length 4 from which the first two indexes are not present. `"0" in newArray` would result in `false`. – 3limin4t0r Mar 06 '21 at 21:38
  • 3limin4t0r good call out. I used `null` to denote `unset`. However, defining `newArray = [ , , 'new', 'ehrray']` does seem to produce the same outcome, simply without the null values. – Logan C Mar 06 '21 at 21:48
  • 2
    @LoganC the spread syntax is very powerfull and expressive in a very limited way. It does create a shallow merge, not a deep one, so `merged = { ...obj, ...newObj };` should rather be: `merged = { ...obj, ...newObj, key: { ...obj.key, ...newObj.key } }`. The way you deep-merge two objects can vary a lot depending on what you're trying to express with that merge, therefore there's no built-in solution for that. Some libraries have implemented a deep-merge function, but they always express the developers opinion on how you're supposed to merge two objects; That may not always be what you want. – Thomas Mar 06 '21 at 22:09
  • Got it, thanks Thomas. I think this is likely what I'm looking to do. – Logan C Mar 06 '21 at 22:17

3 Answers3

0

This might be useful

const a0 = ['1', '2', undefined , undefined, '5', '6', '7'];
const a1 = [undefined, undefined, '3', '4'];

function merge(a, b) {
  return a.map(function(v,i){ return v?v:b[i]});
}

console.log(a0 > a1?merge(a0, a1):merge(a1, a0));
Reflective
  • 3,854
  • 1
  • 13
  • 25
0

I wanted to updated that I ended up going with a recursive merge to get the nested object containing an array merged.

const array = ['an', 'array'];
const newArray = [, , 'new', 'ehrray'];

const obj = {
  key: { ...array
  }
};

const newObj = {
  key: { ...newArray
  }
};

const merge = (obj1, obj2) => {

  const recursiveMerge = (obj, entries) => {
    for (const [key, value] of entries) {
      if (typeof value === "object") {
        obj[key] = obj[key] ? { ...obj[key]
        } : {};
        recursiveMerge(obj[key], Object.entries(value))
      } else {
        obj[key] = value;
      }
    }

    return obj;
  }

  return recursiveMerge(obj1, Object.entries(obj2))
}

console.log(merge(obj, newObj));
Logan C
  • 41
  • 3
-1

The idea is that there are unset values with only a few set. eg. const newArray = new Array(4); newArray[2] = 'new';

{ value: null }, even { value: undefined } is not the same thing as { foo: 42 } with no value at all. That's the reason that in your example "an" and "array" are overwritten with the nulls from the newArray.

This particular example you can solve by swapping the order in which you add the arrays to the result, but as soon as both arrays contain null-values there is no way to do it with spread-syntax / Object.assign alone. You have to implement the behaviour:

const array = new Array('an', 'array', null, null, "and", "more", "from", "array");
const newArray = new Array(null, null, 'new', 'ehrray');

function merge(a, b) {
  const result = [];
  for (let i = 0; i < a.length || i < b.length; ++i) {
    result[i] = b[i] == null ? a[i] : b[i];
  }
  return result;
}

console.log(merge(array, newArray));
Thomas
  • 11,958
  • 1
  • 14
  • 23