Combine/merge objects in array based upon common v

2019-05-14 08:59发布

问题:

I have an array like the following.

[ { sku: 'TEA-BLCK', price: '10', quantity: '1' },
  { sku: 'TEA-ELGY', price: '10', quantity: '1' },
  { sku: 'TEA-CHAI', price: '10', quantity: '1' },
  { sku: 'TEA-GREN', price: '10', quantity: '1' },
  { sku: 'TEA-ELGY', price: '10', quantity: '1' },
  { sku: 'TEA-MINT', price: '10', quantity: '1' } ]

I need to make it look like this

[ { sku: 'TEA-BLCK', price: '10', quantity: '1' },
  { sku: 'TEA-ELGY', price: '10', quantity: '2' },
  { sku: 'TEA-CHAI', price: '10', quantity: '1' },
  { sku: 'TEA-GREN', price: '10', quantity: '1' },
  { sku: 'TEA-MINT', price: '10', quantity: '1' } ]

I got so far to make this reduce function using underscore.js.

var reduce = function(){
    return _.reduce(line_items, function(quant, item) {
        quant[item.sku] = (typeof(quant[item.sku]) !== "undefined") ? quant[item.sku] : 0 ;
        quant[item.sku] = parseFloat(item.quantity) + quant[item.sku];
        return quant;
    }, {});
}

Which spits out the following.

{ 'TEA-BLCK': 1,
  'TEA-ELGY': 1,
  'TEA-CHAI': 2,
  'TEA-GREN': 1,
  'TEA-MINT': 1 }

Is this a good job for reduce? How can I get it the way I want?

回答1:

Try something like this:

var line_items = [
    { sku: 'TEA-BLCK', price: '10', quantity: 2 },
    { sku: 'TEA-ELGY', price: '10', quantity: 3 },
    { sku: 'TEA-CHAI', price: '10', quantity: 1 },
    { sku: 'TEA-GREN', price: '10', quantity: 1 },
    { sku: 'TEA-ELGY', price: '10', quantity: 1 },
    { sku: 'TEA-MINT', price: '10', quantity: 1 }
];

// Group items by sku.
var line_items_by_sku = _(line_items).groupBy(function (item) { return item.sku; });

// Get the new line_items array based on the grouped data.
line_items = _(line_items_by_sku).map(function (items) {
    var item = items[0];
    item.quantity = _.reduce(items, function(memo, item){ return memo + item.quantity; }, 0);
    return item;
});

See this jsfiddle.



回答2:

What about a simple solution like this:

function reduce(array) {
    var out = [];
    var indexBySku = {};
    for (var i = array.length; i--;) {
        if (!indexBySku[array[i].sku]) {
            indexBySku[array[i].sku] = out.length;
            out.push(array[i]);
        } else {
            out[indexBySku[array[i].sku]].quantity -= -array[i].quantity;
        }
    }
    return out;
}

http://jsfiddle.net/waV6H/1

Probably the only thing that needs explanation here is the line where we subtract a negative instead of adding. This is just to avoid string concatenation, since the values in "quantity" are strings.


Super condensed version, dropping unneeded braces and semicolons... just for fun. Preserves order and quantity as string, works with quantity != 1.

function reduce(array) {
    var out = [], indexBySku = {}, len = array.length, i, j
    for (i = 0; i < len; i++)
        (j = indexBySku[array[i].sku]) ?  
            out[j].quantity = out[j].quantity - -array[i].quantity + '' :
            indexBySku[array[i].sku] = out.push(array[i]) - 1
    return out
}


回答3:

I was a bit late to the party, but it sounded so fun, so..

JSON.stringify(
[ { sku: 'TEA-BLCK', price: '10', quantity: '1' },
  { sku: 'TEA-ELGY', price: '10', quantity: '1' },
  { sku: 'TEA-CHAI', price: '10', quantity: '1' },
  { sku: 'TEA-GREN', price: '10', quantity: '1' },
  { sku: 'TEA-ELGY', price: '10', quantity: '1' },
  { sku: 'TEA-MINT', price: '10', quantity: '1' } ].sort( function (a, b) {
   return a.sku < b.sku;
} ).reduce( function (a,v) {
   if ( !a[0] || a[0].sku !== v.sku  ) {
      a.unshift(v);
   } else {
      // notice the very inefficient str -> number -> str conversion
      a[0].quantity = 1*a[0].quantity + 1*v.quantity + '';
   }

   return a;
 }, []) );

Here we pre-sort the array, and then add elements to the beginning of accumulator. And GGG have already said about strings.

http://jsfiddle.net/EqGUe/



回答4:

A bit late as well, but fun to work out http://jsbin.com/amazat/2/

line_items_reduced = _.map(reduce(), function(value, key) {
  var line_item = _.find(line_items, function(line_i){
    return (key === line_i.sku);
  });
  line_item.quantity = value;
  return line_item;
});


回答5:

This updates benekastah's answer and handles quantities more than 1... But I did take the liberty to change quantity to an integer

http://jsfiddle.net/xjUDY/3/

var line_items = [
    { sku: 'TEA-BLCK', price: '10', quantity: 2 },
    { sku: 'TEA-ELGY', price: '10', quantity: 3 },
    { sku: 'TEA-CHAI', price: '10', quantity: 1 },
    { sku: 'TEA-GREN', price: '10', quantity: 1 },
    { sku: 'TEA-ELGY', price: '10', quantity: 1 },
    { sku: 'TEA-MINT', price: '10', quantity: 1 }
];

// Group items by sku.
var line_items_by_sku = _(line_items).groupBy(function (item) { return item.sku; });

// Get the new line_items array based on the grouped data.
line_items = _(line_items_by_sku).map(function (items) {
    var item = items[0];
    item.quantity = _.reduce(items, function(memo, item){ return memo + item.quantity; }, 0);
    return item;
});

document.write("<pre>"+JSON.stringify(line_items, null, " ")+"</pre>");
​


回答6:

Technically this is the complete answer. From string to string. Sorry guys.

var line_items = [
    { sku: 'TEA-BLCK', price: '10', quantity: "2" },
    { sku: 'TEA-ELGY', price: '10', quantity: "3" },
    { sku: 'TEA-CHAI', price: '10', quantity: "1" },
    { sku: 'TEA-GREN', price: '10', quantity: "1" },
    { sku: 'TEA-ELGY', price: '10', quantity: "1" },
    { sku: 'TEA-MINT', price: '10', quantity: "1" }
];

// Group items by sku.
var line_items_by_sku = _(line_items).groupBy(function (item) { return item.sku; });

// Get the new line_items array based on the grouped data.
line_items = _(line_items_by_sku).map(function (items) {
    var item = items[0];
    item.quantity = _.reduce(items, function(memo, item){ return memo + parseFloat(item.quantity); }, 0);
    item.quantity = item.quantity.toString();
    return item;
});

document.write("<pre>"+JSON.stringify(line_items, null, " ")+"</pre>");