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