Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

removing duplicate objects in an array, keeping ones with maximum property value

I have an array like this:

const array=[ {id:0, quantity:1}, {id:1, quantity:2}, {id:0, quantity:4} ]

My GOAL is to be like this:

const array=[ {id:1, quantity:2}, {id:0, quantity:4} ]

The order of the object does not matter as long as it can find the 'id' with the larger quantity

I tried filter + findIndex, map +filter, etc but I kept making mistake. I need help.

like image 438
william anputra Avatar asked Oct 25 '25 16:10

william anputra


2 Answers

You could use a hash table and check if an object with the same id is in the result set. If the actual quantity is greater, assign the actual object.

var array = [{ id: 0, quantity: 1 }, { id: 1, quantity: 2 }, { id: 0, quantity: 4 }],
    hash = Object.create(null),
    unique = array.reduce(function (r, o) {
        if (!(o.id in hash)) {
            hash[o.id] = r.push(o) - 1;
            return r;
        }
        if (o.quantity > r[hash[o.id]].quantity) {
            r[hash[o.id]] = o;
        }
        return r;
    }, []);
    
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 89
Nina Scholz Avatar answered Oct 28 '25 06:10

Nina Scholz


Let's get all the ids first. Then for each unique id, we'll find all the relevant objects in the array (using filter) and get the maximum of all their quantity properties.

function uniqByMax(arr) {
  const ids = arr.map(elt => elt.id);
  const uniqueIds = uniq(ids);

  return uniqueIds.map(id => {
    const matchingElts = arr.filter(elt => elt.id === id);
    const quantities = matchingElts.map(elt => elt.quantity);
    const quantity = Math.max(...quantities);

    return {id, quantity};
  });
}

You can grab uniq off the net somewhere, or use a library, or write it yourself.

Here is another approach, which uses a filter with side effects, if that is your cup of tea:

function uniqByMax(arr) {
  return arr.filter(elt => {
    const previous = arr.find(e2 => e2.id === elt.id);
    if (previous === elt) return true;
    previous.quantity = Math.max(previous.quantity, elt.quantity);
  });
}

The basic idea is to loop through the elements. For each element, we find if there is an earlier element with the same id. If not, retain this element (return true;); otherwise, update the quantity of the earlier element we retained with the maximum of its quantity and this element's quantity.

In the interest of generality, it could be interesting to parameterize this function by the property we are finding unique values of, and the way to update/combine multiple items:

function uniqTransform(arr, prop, combine) {
  return arr.filter(elt => {
    const previous = arr.find(e2 => e2[prop] === elt[prop]);
    if (previous === elt) return true;
    combine(previous, elt);
  });

Then we call this with

uniqTransform(arr, 'id', (a, b) => a.quantity = Math.max(a.quantity, b.quantity));

Or we could generalize it further by using another function to identify elements which are supposed to be considered the same for uniqueifying purposes, which we will call uniqueFunc:

function uniqTransform(arr, uniqueFunc, combineFunc) {
  return arr.filter(elt => {
    const previous = arr.find(e2 => uniqueFunc(elt, e2));
    if (previous === elt) return true;
    combineFunc(previous, elt);
  });

Then we call this with

uniqTransform(
  arr, 
  (a, b) => a.id === b.id, 
  (a, b) => a.quantity = Math.max(a.quantity, b.quantity));