Zafar
Zafar

Reputation: 321

JavaScript ES6 get sum of values being mapped in array in the same loop iteration

I have a unique challenge that I have been trying to solve. I have an object like this:

    obj = 
    [
      {item: "Skirt", price: 290}, 
      {item: "Shoes", price: 300}, 
      {item: "Skirt", price: 450}, {item: "Jeans", price: 35}
    ]

I am mapping this to get count of duplicate items with their respective average price, so that the desired output looks like:

UPDATE:

obj = 
[
 {
 Skirt: 2, 
 price: 370, //2 being the count of skirt items, 370 average price
 Jeans: 1, 
 Price: 35, 
 Shoes: 1, 
 Price: 30
 }
]

I am using this function to map the items:

Av_price = function(values) {
    let new_val = new Map();
       for (let x of values)
         new_val.set([x["item"]],  [(new_val.get(x["item"])|| 0) +1), (new_val.get(x["price"] || x["price"])/x["price"]])
return [...new_val.entries()];
};

The problem here is: I am able to count the number of items, say skirts, but unable to get the second value straight, I mean the prices; so that I can divide them by total number of items (which, in this example, are "2" for skirts, for example)! This unique challenge has boggled my mind for two weeks. Is there any way to get the count of all the skirt being mapped in the same loop iteration and then divide the sum of prices by their count to get an average price?

I will be hugely thankful for the help!

Upvotes: 0

Views: 413

Answers (3)

Nina Scholz
Nina Scholz

Reputation: 386868

You could take a hash table for same items and build the average out of the stored values.

var data = [{ item: "Skirt", price: 290 }, { item: "Shoes", price: 300 }, { item: "Skirt", price: 450 }, { item: "Jeans", price: 35 }],
    hash = Object.create(null),
    result = [];

data.forEach(function (o) {
    var ref;
    if (!hash[o.item]) {
        hash[o.item] = [{ item: o.item }, [0, 0]]
        result.push(hash[o.item]);
    }
    ref = hash[o.item][1];
    ref[1] = (ref[0] * ref[1] + o.price) / ++ref[0];
});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

With a changed data structure for the result, you yould omit the hash table and take the result object directly.

var data = [{ item: "Skirt", price: 290 }, { item: "Shoes", price: 300 }, { item: "Skirt", price: 450 }, { item: "Jeans", price: 35 }],
    result = data.reduce(function (o, { item, price }) {
        o[item] = o[item] || 0;
        o[item + 'Price'] = (o[item] * (o[item + 'Price'] || 0) + price) / ++o[item];
        return o;
    }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Ori Drori
Ori Drori

Reputation: 193120

Use Array#reduce to create the map, which will contain the count and the summed price for each item. Then spread the map to entries, and Array#map it to the requested format:

const arr = [
  {item: "Skirt", price: 290}, 
  {item: "Shoes", price: 300}, 
  {item: "Skirt", price: 450}, 
  {item: "Jeans", price: 35}
];

const Av_price = (values) => [...
  new Map(values.reduce((map, { item, price }) => {
    const o = map.get(item) || { sum: 0, count: 0 };
    o.count++;
    o.sum += price;

    return map.set(item, o);
  }, new Map()))
].map(([item, { count, sum }]) => [item, [count, sum/count]]);

const result = Av_price(arr);

console.log(result);

Upvotes: 1

Jared Smith
Jared Smith

Reputation: 22030

let totalItems = obj.reduce((acc, {name, price}) => {
  if (!acc[name]) {
    acc[name] = 0;
    acc[`${name}Price`] = price;
  }
  acc[name] += 1;
  return acc;
}, {});

now you have the price of each item and the total number of each. The totalItems object will look something like this:

{
  skirt: 6,
  skirtPrice: 290,
  shoes: 3,
  shoesPrice: 300
}

and so on.

Upvotes: 1

Related Questions