0

I have an array

[
  { price: 10 },
  { price: 10 },
  { price: 10 },
  { price: 10 },
  { price: 20 },
  { price: 20 },
]

and I want it transformed into

[
  { numElements: 4, price: 10 },
  { numElements: 2, price: 20 },
]

I have tried using arr.reduce((prev, curr) => ..., []) to accomplish this, but I can't figure out how to do it.

Jamgreen
  • 10,329
  • 29
  • 113
  • 224
  • Can you show the attempt with reduce? Using that is a very good start (not being condescending). – evolutionxbox Aug 07 '17 at 15:49
  • Possible duplicate of [How to group an array of objects by key](https://stackoverflow.com/questions/40774697/how-to-group-an-array-of-objects-by-key) – Heretic Monkey Dec 19 '18 at 17:42

5 Answers5

0
var hash = {}, result = [];

 arr.forEach(function(el){
   if(hash[el.price]){
     hash[el.price].numElements++;
   }else{
     result.push(hash[el.price]={price:el.price,numElements:1});
  }
});

Run

May use a hash table for price lookup. Or with reduce and find:

 arr.reduce((res,{price})=>
  (( res.find(el=>el.price===price) || res[res.push({price,numElements:0})-1] )
  .numElements++,res)
);

Run

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

A traditional method might use a for/loop to wrangle the data, but these days JavaScript has a number of functional methods that can help. This code uses reduce and map. To get your data in the format you want is a two stage process.

First, use reduce to create a hash table using the price as a key (because you know the each price is going to be unique:

const obj = arr.reduce((p, c) => {

  // If price exists as a key its value by 1
  // otherwise set it to 1.
  p[c.price] = ++p[c.price] || 1;
  return p;
}, {});

OUTPUT

{
  "10": 4,
  "20": 2
}

As it stands you've got a perfectly good object that you can access by the key/price and I would probably just stop there:

obj['10'] // 4

But if you want to get that data into the format in your question, map over the object keys to return an array of new objects.

const out = Object.keys(obj).map(key => {
  return { price: +key, numElements: obj[key] };
});

DEMO

Andy
  • 61,948
  • 13
  • 68
  • 95
0

You can use try this:

let arr = [
  { price: 10 },
  { price: 10 },
  { price: 10 },
  { price: 10 },
  { price: 20 },
  { price: 20 },
]

let result = []

let counter = {}

arr.forEach( el => {

  if (!counter[el.price]) counter[el.price] = 1
  else counter[el.price]++

  console.log(counter[el.price])

})

for (let id in counter) {
  result.push({numElements: counter[id], price: id})
}
Gabriel Carneiro
  • 643
  • 5
  • 16
0

Assuming that the data comes sorted on price property, with a single .reduce() you may do as follows;

var data   = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
    result = data.reduce((r,d,i) => i ? r[r.length-1].price === d.price ? (r[r.length-1].numElemenets++, r)
                                                                        : (r.push(Object.assign({}, d, {numElemenets: 1})),r)
                                      : [Object.assign({}, d, {numElemenets: 1})], {});
console.log(result);
Redu
  • 25,060
  • 6
  • 56
  • 76
0

You could look up the price in the result array and if not found insert a new object.

var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
    grouped = data.reduce((r, { price }) => {
        var t = r.find(p => price === p.price);
        t || r.push(t = { numElements: 0, price });
        t.numElements++;
        return r;
    }, []);
    
console.log(grouped);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392