Update if exists or add new element to array of objects - elegant way in javascript + lodash
Solution 1
You can use an object instead of an array:
var hash = {
'1': {uid: 1, name: "bla", description: "cucu"},
'2': {uid: 2, name: "smth else", description: "cucarecu"}
};
The keys are the uids. Now your function addOrReplace
is simple like this:
function addOrReplace(hash, object) {
hash[object.uid] = object;
}
UPDATE
It's also possible to use an object as an index in addition to the array.
This way you've got fast lookups and also a working array:
var arr = [],
arrIndex = {};
addOrReplace({uid: 1, name: "bla", description: "cucu"});
addOrReplace({uid: 2, name: "smth else", description: "cucarecu"});
addOrReplace({uid: 1, name: "bli", description: "cici"});
function addOrReplace(object) {
var index = arrIndex[object.uid];
if(index === undefined) {
index = arr.length;
arrIndex[object.uid] = index;
}
arr[index] = object;
}
Take a look at the jsfiddle-demo (an object-oriented solution you'll find here)
Solution 2
In your first approach, no need for Lodash thanks to findIndex()
:
function upsert(array, element) { // (1)
const i = array.findIndex(_element => _element.id === element.id);
if (i > -1) array[i] = element; // (2)
else array.push(element);
}
Example:
const array = [
{id: 0, name: 'Apple', description: 'fruit'},
{id: 1, name: 'Banana', description: 'fruit'},
{id: 2, name: 'Tomato', description: 'vegetable'}
];
upsert(array, {id: 2, name: 'Tomato', description: 'fruit'})
console.log(array);
/* =>
[
{id: 0, name: 'Apple', description: 'fruit'},
{id: 1, name: 'Banana', description: 'fruit'},
{id: 2, name: 'Tomato', description: 'fruit'}
]
*/
upsert(array, {id: 3, name: 'Cucumber', description: 'vegetable'})
console.log(array);
/* =>
[
{id: 0, name: 'Apple', description: 'fruit'},
{id: 1, name: 'Banana', description: 'fruit'},
{id: 2, name: 'Tomato', description: 'fruit'},
{id: 3, name: 'Cucumber', description: 'vegetable'}
]
*/
(1) other possible names: addOrReplace()
, addOrUpdate()
, appendOrUpdate()
, insertOrUpdate()
...
(2) can also be done with array.splice(i, 1, element)
Note that this approach is "mutable" (vs "immutable"): it means instead of returning a new array (without touching the original array), it modifies directly the original array.
Solution 3
I personally do not like solutions that modify the original array/object, so this is what I did:
function addOrReplaceBy(arr = [], predicate, getItem) {
const index = _.findIndex(arr, predicate);
return index === -1
? [...arr, getItem()]
: [
...arr.slice(0, index),
getItem(arr[index]),
...arr.slice(index + 1)
];
}
And you would use it like:
var stuff = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
];
var foo = { id: 2, foo: "bar" };
stuff = addOrReplaceBy(
stuff,
{ id: foo.id },
(elem) => ({
...elem,
...foo
})
);
What I decided to do was to make it more flexible:
- By using
lodash -> _.findIndex()
, the predicate can be multiple things - By passing a callback
getItem()
, you can decide whether to fully replace the item or do some modifications, as I did in my example.
Note: this solution contains some ES6 features such as destructuring, arrow functions, among others.
Solution 4
Maybe
_.mixin({
mergeById: function mergeById(arr, obj, idProp) {
var index = _.findIndex(arr, function (elem) {
// double check, since undefined === undefined
return typeof elem[idProp] !== "undefined" && elem[idProp] === obj[idProp];
});
if (index > -1) {
arr[index] = obj;
} else {
arr.push(obj);
}
return arr;
}
});
and
var elem = {uid: 3, name: 'new element name name', description: "cocoroco"};
_.mergeById(arr, elem, "uid");
Solution 5
Old post, but why not use the filter function?
// If you find the index of an existing uid, save its index then delete it
// --- after the filter add the new object.
function addOrReplace( argh, obj ) {
var index = -1;
argh.filter((el, pos) => {
if( el.uid == obj.uid )
delete argh[index = pos];
return true;
});
// put in place, or append to list
if( index == -1 )
argh.push(obj);
else
argh[index] = obj;
}
Here is a jsfiddle showing how it works.
ganqqwerty
Updated on November 04, 2021Comments
-
ganqqwerty over 2 years
So I have an array of objects like that:
var arr = [ {uid: 1, name: "bla", description: "cucu"}, {uid: 2, name: "smth else", description: "cucarecu"}, ]
uid
is unique id of the object in this array. I'm searching for the elegant way to modify the object if we have the object with the givenuid,
or add a new element, if the presenteduid
doesn't exist in the array. I imagine the function to be behave like that in js console:> addOrReplace(arr, {uid: 1, name: 'changed name', description: "changed description"}) > arr [ {uid: 1, name: "bla", description: "cucu"}, {uid: 2, name: "smth else", description: "cucarecu"}, ] > addOrReplace(arr, {uid: 3, name: 'new element name name', description: "cocoroco"}) > arr [ {uid: 1, name: "bla", description: "cucu"}, {uid: 2, name: "smth else", description: "cucarecu"}, {uid: 3, name: 'new element name name', description: "cocoroco"} ]
My current way doesn't seem to be very elegant and functional:
function addOrReplace (arr, object) { var index = _.findIndex(arr, {'uid' : object.uid}); if (-1 === index) { arr.push(object); } else { arr[index] = object; } }
I'm using lodash, so I was thinking of something like modified
_.union
with custom equality check.