PGT
PGT

Reputation: 2029

Add and combine duplicates in array of objects with lodash with specific conditions

Say I have a sorted array of objects like this:

[
  {name: 'item1', quantity: 5},
  {name: 'item1', quantity: 8},
  {name: 'item2', quantity: 6},
  {name: 'item2', quantity: 3},
  {name: 'item3', quantity: 1},
  {name: 'item3', quantity: 1},
]

I want to add up the values of items which have quantities > 1 and combine them so I get this:

[
  {name: 'item1', quantity: 13},
  {name: 'item2', quantity: 9},
  {name: 'item3', quantity: 1},
  {name: 'item3', quantity: 1},
]

Is there a quick single or chain of lodash methods that can achieve this? I was thinking to use _.map but it doesn't give you previous item, so I'd have to use a variable outside the _.map scope to keep that value. Seeing if I can do this with lodash since I'm already using it for other methods and not write extra lines of code.

If the extra condition is not possible, then combining and adding all items will have to do.

Upvotes: 1

Views: 1317

Answers (4)

Frank Tan
Frank Tan

Reputation: 4412

This is my attempt using only lodash:

var source = [
  {name: 'item1', quantity: 5},
  {name: 'item1', quantity: 8},
  {name: 'item2', quantity: 6},
  {name: 'item2', quantity: 3},
  {name: 'item3', quantity: 1},
  {name: 'item3', quantity: 1},
];

var predicate = function (e) {
  return e.quantity > 1;
};
var result = _.chain(source)
  .filter(predicate)
  .groupBy(function (e) {
    return e.name;
  })
  .map(function (group) {
    return _.reduce(group, function (current, next) {
      return {
        name: next.name,
        quantity: current.quantity + next.quantity
      };
    });
  })
  .union(_.filter(source, function (e) {
    return !predicate(e);
  }))
  .value();

document.getElementById("output").textContent = JSON.stringify(result, 0, 2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>
<pre id="output"></pre>

Not sure if this is the most efficient way, as I'm not deeply familiar with lodash. Basically, the idea is this:

  1. Get the elements with quantity > 1
  2. Group by name
  3. Produce sums for each name
  4. Union with the elements with quantity <= 1

Fiddle

Upvotes: 4

Nina Scholz
Nina Scholz

Reputation: 386600

Another solution in plain Javascript with a single loop.

var data = [{ name: 'item1', quantity: 5 }, { name: 'item1', quantity: 8 }, { name: 'item2', quantity: 6 }, { name: 'item2', quantity: 3 }, { name: 'item3', quantity: 1 }, { name: 'item3', quantity: 1 }],
    combined = function (array) {
        var r = [];
        array.forEach(function (a, i) {
            if (a.quantity === 1) {
                r.push(a);
                return;
            }
            if (!this[a.name]) {
                this[a.name] = { name: a.name, quantity: 0 };
                r.push(this[a.name]);
            }
            this[a.name].quantity += a.quantity;
        }, {});
        return r;
    }(data);

document.write('<pre>' + JSON.stringify(combined, 0, 4) + '</pre>');

Upvotes: 1

Ru Hasha
Ru Hasha

Reputation: 956

Here is a pure lodash-ian solution :)
It uses chain, reduce, toPairs, map and 1 temporary variable to do the job.

items = [
  {name: 'item1', quantity: 5},
  {name: 'item1', quantity: 8},
  {name: 'item2', quantity: 6},
  {name: 'item2', quantity: 3},
  {name: 'item3', quantity: 1},
  {name: 'item3', quantity: 1}
];

summary = _.chain(items).reduce(function(acc, i) {
  if (i.quantity > 0) {
    acc[i.name] = (acc[i.name] || 0) + i.quantity;
  }
  return acc;
}, {}).toPairs().map(function(x) {
  var tmp = {};
  tmp[x[0]] = x[1];
  return tmp;
}).value();

console.log(JSON.stringify(summary));
// Outputs:  [{"item1":13},{"item2":9},{"item3":2}]

Upvotes: 1

guest271314
guest271314

Reputation: 1

You can use Array.prototype.forEach() , for loop

var arr = [
  {name: 'item1', quantity: 5},
  {name: 'item1', quantity: 8},
  {name: 'item2', quantity: 6},
  {name: 'item2', quantity: 3},
  {name: 'item3', quantity: 1},
  {name: 'item3', quantity: 1},
];

var res = [];

arr.forEach(function(item, index) {
  if (res.length === 0 
      || !res.some(function(elem) {return elem.name === item.name}) 
      || item["quantity"] === 1 ) {
    res.push(item)
  } else {
    for (var i = 0; i < res.length; i++) {
      if (res[i]["name"] === item["name"] 
          && (res[i]["quantity"] !== 1 && item["quantity"] !== 1)) {
        res[i]["quantity"] += item["quantity"]
      }
    }
  }
});

document.querySelector("pre").textContent = JSON.stringify(res, null, 2)
<pre></pre>

Upvotes: 0

Related Questions