8
\$\begingroup\$

I am trying to write a basic function that will decide whether the object already exists in array of objects based on unique id that is inside this object as a property. Upon searching I came up with this code, and it seems to be doing the job fine, but I can imagine there is a better and faster way to determine whether to push or update the object. If you could share your best answer for this please...

var myArray = [
  {
    id: 1,
    car: "Ford"
  },
  {
    id: 2,
    car: "Honda"
  }
];

var objToOverwrite = {
  id: 1,
  car: "Bentley" // to overwrite car: "Ford"
};

var objToPush = {
  id: 3,
  car: "Toyota"
};


function pushToArray ( arr, obj ) {


  var existingIds = arr.map((obj) => obj.id);

    if (! existingIds.includes(obj.id)) {
      arr.push(obj);
    } else {
      arr.forEach((element, index) => {
        if (element.id === obj.id) {
          arr[index] = obj;
        };
      });
    };
};

pushToArray ( myArray, objToPush );

console.log(myArray);
\$\endgroup\$
1

3 Answers 3

19
\$\begingroup\$

Your pushToArray function is less efficient than it could be because it it looping over the entire array multiple times. We could instead use find()/findIndex() to loop over the array just once:

function pushToArray(arr, obj) {
    const index = arr.findIndex((e) => e.id === obj.id);

    if (index === -1) {
        arr.push(obj);
    } else {
        arr[index] = obj;
    }
}

This is still O(n) performance, but does less repeated work and does not require altering the data structure you are using.

If you need better performance than that, you can keep the array sorted by ID and implement a simple binary search, or you can change your data structure to one more optimized for fast lookups.

\$\endgroup\$
5
\$\begingroup\$

You can convert an array to map and access map by id, for example:

var myArray = [
  {
    id: 1,
    car: "Ford"
  },
  {
    id: 2,
    car: "Honda"
  }
];

Can be converted to map:

var result = myArray.reduce(function(map, obj) {
    map[obj.id] = obj.car;
    return map;
}, {});

The result will be:

{
  1:"Ford",
  2:"Honda"
}

Then just this:

result[obj.id] = obj.car;

If the element hasn't existed it will be added, if it exists, it will be updated.

\$\endgroup\$
3
\$\begingroup\$

If you know there is only one item don't use a loop that you can not break out of.

function pushToArray ( arr, obj ) {
    var existingIds = arr.map((obj) => obj.id);
    if (! existingIds.includes(obj.id)) {
        arr.push(obj);
    } else {
        arr.forEach((element, index) => { // You are forced to iterate all items
            if (element.id === obj.id) {
               arr[index] = obj;
            };   // The ; is not needed after } unless its an Object literal
        });  
    }; // again ; not needed
}; // ; not needed

Use a for loop rather than Array.forEach when you need to break out of the loop early

//...
   var index = 0;
   for (const element of arr) {
       if (element.id === obj.id) {
           arr[index] = obj;
           break;   // found and replaced, nothing more to do so break out of loop
       }
       index ++;
   }

The id is unique so all you need to do is get the item index by id. Array.findIndex will do that for you and then either replace an existing item or push the new. This will save you having to create the extra array of existing ids

function pushToArray(arr, obj) {
    const index = arr.findIndex(item => item.id === obj.id);
    if (index > -1) { arr[index] = obj }
    else { arr.push(obj) }
}

Personally I would store the data not in an array but as a Map Then you don't have to do anything but set by id

// the array as a map with id as the index;
function addItem(map, obj) {
    map.set(obj.id, obj);  // will replace existing if id is already used
                           // or add if the id is not used.
}

You can convert from the array to the map as follows

const myMap = new Map();
for (const item of myArray) { myMap.set(item.id, item) };
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.