Mark Sandman
Mark Sandman

Reputation: 3333

JavaScript to remove duplicate objects in an Object Array & sum the properties

I've been trying for days now to come up with a solution to my problem and I just can't, imagine I have the following JSON array (we shall call this jsonData:

[
  { "id": 118748, "price":"", "stocklevel": 100,   "instock": false, "pname": "Apple TV" },
  { "id": 118805291, "price":"", "stocklevel": 432,   "instock": true, "pname": "Hitachi TV"},
  { "id": 118801891, "price":"", "stocklevel": 0,   "instock": false, "pname": "Sony TV" },
  { "id": 118748, "price":"", "stocklevel": 2345,   "instock": true, "pname": "Apple TV"},
...

Now I may have over 100 items in my JSON array, I want to remove items which have a duplicate id, but sum the stock levels and retain the order in the array so a row should take the place of the latest occurrence of that id. In the above JSON the first instance of the object with "id": 118748 is removed but it's stock level value passed / added the next instance of an object with the same id, so the JSON Array would look like so:

[
  { "id": 118805291, "price":"", "stocklevel": 432,   "instock": true, "pname": "Hitachi TV"},
  { "id": 118801891, "price":"", "stocklevel": 0,   "instock": false, "pname": "Sony TV" },
  { "id": 118748, "price":"", "stocklevel": 2445,   "instock": true, "pname": "Apple TV"},
...

I produced the following code to remove the duplicates, but I can't sum the stock level totals, here is my code:

function idsAreEqual(obj1, obj2) {
    return obj1.id === obj2.id;
}

function arrayContains(arr, val, equals) {
    var i = arr.length;
    while (i--) {
        if (equals(arr[i], val)) {
            return true;
        }
    }
    return false;
}

function removeDups(arr, equals) {
    var originalArr = arr.slice(0);
    var i, k, len, val;
    arr.length = 0;

    for (i = originalArr.length - 1, len = originalArr.length, k  = originalArr.length - 1 ; i > 0; --i) {
        val = originalArr[i];

        if (!arrayContains(arr, val, equals)) {
            arr.push(val);
        }
    }
}


removeDups(jsonData, idsAreEqual);
jsonData.reverse();

Can someone please help me solve this problem? Please note that I cannot use Underscore, jQuery or any other library.

Big thanks in advance

Upvotes: 0

Views: 1479

Answers (1)

David Atchley
David Atchley

Reputation: 1204

You can do something like the following, which is a little simpler than your implementation I think. I also use forEach and reduce, which are ES5 but should work on modern browsers. You can grab a polyfil for either if you're working with older, non-ES5 compliant browsers:

var data = [
  { "id": 118748, "price":"", "stocklevel": 0,   "instock": false, "pname": "Apple TV" },
  { "id": 118805291, "price":"", "stocklevel": 432,   "instock": true, "pname": "Hitachi TV"},
  { "id": 118801891, "price":"", "stocklevel": 0,   "instock": false, "pname": "Sony TV" },
  { "id": 118748, "price":"", "stocklevel": 2345,   "instock": true, "pname": "Apple TV"}
];

function dedup_and_sum(arr, prop) {
    var seen = {},
        order = [];
    arr.forEach(function(o) {
        var id = o[prop];
        if (id in seen) {
            // keep running sum of stocklevel
            var stocklevel = seen[id].stocklevel + o.stocklevel
            // keep this newest record's values
            seen[id] = o;  
            // upid[118805291], stocklevel=432, instock=truedate stocklevel to our running total
            seen[id].stocklevel = stocklevel;
            // keep track of ordering, having seen again, push to end
            order.push(order.splice(order.indexOf(id), 1));
        }
        else {
            seen[id] = o;
            order.push(id);
        }
    });

    return order.map(function(k) { return seen[k]; });
}

// Get unique records, keeping last record of dups
// and summing stocklevel as we go
var unique = dedup_and_sum(data, 'id');
unique.forEach(function(o) {
    console.log("id[%d], stocklevel=%d, instock=%s", o.id, o.stocklevel, o.instock);
});
// output =>
// id[118805291], stocklevel=432, instock=true
// id[118801891], stocklevel=0, instock=false 
// id[118748], stocklevel=2445, instock=true

EDIT Updated to match requirements we discussed in comments.

Upvotes: 4

Related Questions