Skip to content
Advertisement

Overwrite array object if already present else add to array

When I run this example I expect the elementThatAllreadyExist to overwrite the existing object with the name test1 which it does correctly.

When I run the example with arr2, I expect the newElement element to be added to the array since no object with the name "test33" exists in the array, but no element is added to the array.

Why is it ignoring my || obj fallback?

let arr = [{
    name: "test1",
    values: {
      value1: 1,
      value2: 2,
      value3: 3,
    }
  },
  {
    name: "test2",
    values: {
      value1: 1,
      value2: 2,
      value3: 3,
    }
  },
  {
    name: "test3",
    values: {
      value1: 1
    }
  },
  {
    name: "test4",
    values: {
      value1: 1,
      value2: 2,
      value3: 3,
    }
  }
]

let elementThatAllreadyExists = [{
  name: "test1",
  values: {
    value1: 3323,
    value2: 2323,
    value3: 2323,
  }
}]

let newElement = [{
  name: "test33",
  values: {
    value1: 666,
    value2: 666,
    value3: 663,
  }
}]

const arr1 = arr.map(obj => newElement.find(o => o.name === obj.name) || obj);
console.log(arr1)

const arr2 = arr.map(obj => elementThatAllreadyExists.find(o => o.name === obj.name) || obj);
console.log(arr2)

Advertisement

Answer

Converting my comments to an answer, when .find() returns null for every element in arr, as it does for newElement, then the || obj fallback branch runs for every element and map returns a shallow copy. So it’s not ignoring || obj at all. In fact, that’s all it’s doing (when the element doesn’t exist).

map doesn’t add elements, it transforms existing elements. You can use push to do that.

As an aside, it’s odd to have newElement and elementThatAllreadyExists as arrays of 1 element. These variable names should be pluralized if they’re supposed to be arrays. If they’re singular, remove the array to make them plain objects. Use const on all variable declarations you don’t plan to reassign (which should be almost all of them).

Here’s the function. If your inputs really are arrays, loop over them and invoke the addOrReplaceExisting function, but be warned that time complexity is quadratic. You might want to use a Map or object keyed by name. The .set() method already implements the “replace or add” behavior, implementing uniqueness on a key. Maps are O(1) for lookup and replacements.

const replaceOrAdd = (arr, el, key="name") => {
  const existingIndex = arr.findIndex(e => e[key] === el[key]);

  if (existingIndex >= 0) {
    arr[existingIndex] = el;
  }
  else {
    arr.push(el);
  }
};

const arr = [{ name: "test1", values: { value1: 1, value2: 2, value3: 3, } }, { name: "test2", values: { value1: 1, value2: 2, value3: 3, } }, { name: "test3", values: { value1: 1 } }, { name: "test4", values: { value1: 1, value2: 2, value3: 3, } } ];
const elementThatAlreadyExists = { name: "test1", values: { value1: 3323, value2: 2323, value3: 2323, } };
const newElement = { name: "test33", values: { value1: 666, value2: 666, value3: 663, } };

replaceOrAdd(arr, elementThatAlreadyExists);
replaceOrAdd(arr, newElement);
console.log(arr);

Immuable version:

const replaceOrAdd = (arr, el, key="name") => {
  const existingIndex = arr.findIndex(e => e[key] === el[key]);

  if (existingIndex >= 0) {
    return Object.freeze([
      ...arr.slice(0, existingIndex),
      el,
      ...arr.slice(existingIndex + 1),
    ]);
  }

  return Object.freeze(arr.concat(el));
};

const arr = Object.freeze([{ name: "test1", values: { value1: 1, value2: 2, value3: 3, } }, { name: "test2", values: { value1: 1, value2: 2, value3: 3, } }, { name: "test3", values: { value1: 1 } }, { name: "test4", values: { value1: 1, value2: 2, value3: 3, } } ]);
const elementThatAlreadyExists = { name: "test1", values: { value1: 3323, value2: 2323, value3: 2323, } };
const newElement = { name: "test33", values: { value1: 666, value2: 666, value3: 663, } };

const arr1 = replaceOrAdd(arr, elementThatAlreadyExists);
const arr2 = replaceOrAdd(arr1, newElement);
console.log(arr2);

Map version:

const arr = [{ name: "test1", values: { value1: 1, value2: 2, value3: 3, } }, { name: "test2", values: { value1: 1, value2: 2, value3: 3, } }, { name: "test3", values: { value1: 1 } }, { name: "test4", values: { value1: 1, value2: 2, value3: 3, } } ];
const elementThatAlreadyExists = { name: "test1", values: { value1: 3323, value2: 2323, value3: 2323, } };
const newElement = { name: "test33", values: { value1: 666, value2: 666, value3: 663, } };

const elemsByName = new Map(arr.map(e => [e.name, e]));
elemsByName.set(
  elementThatAlreadyExists.name,
  elementThatAlreadyExists
);
elemsByName.set(newElement.name, newElement);
console.log([...elemsByName.values()]); // or use .get to get a single element by name
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement