'Format JSON, order by key group using vanilla js

I people, i need help , i dont know how to handle the object to get what I need using vanilla. I need group by "type" for each "elem", and sum price and discount values, and return them in an array. I would appreciate if someone could explain to me how to approach the transformation.

I have this object

{
        "w_elem": [
            {
                "elem": [
                    {"type": "type_a","price": "100","discount": "10"},
                    {"type": "type_b","price": "120","discount": "12"},
                    {"type": "type_a","price": "165","discount": "10"}
                ],
                "user": "1001",
                "userType": "A"
            },
            {
                "elem": [
                    {"type": "type_a","price": "50","discount": "5"},
                    {"type": "type_b","price": "15","discount": "0"}
                ],
                "user": "1011",
                "userType": "B"
            },
            {
                "elem": [
                    {"type": "type_a","price": "20","discount": "5"},
                    {"type": "type_b","price": "15","discount": "0"}
                ],
                "user": "1011",
                "userType": "B"
            }
        ]
    }

Imust return the following array

    [
      {"type": "type_a","price": "335","discount":"30"},
      {"type":"type_b","price": "150","discount": "12"}
    ]

i tried the following but im not able to understand.

let helper = {};
const data = newArr.reduce((acc, currElem) => {
    currElem.elem.forEach(item => {
        if(!helper[currElem.type]) {
            helper[currElem.type] = Object.assign({}, currElem);
            acc.push(helper[currElem.type])
        }else{
            helper[currElem.type].price += currElem.price;
        helper[currElem.type].discount += currElem.discount;
        }
    })
    return acc;
}, [])


Solution 1:[1]

The following code snippet should do the trick.

As for the explanation:

  1. You want to get a list of all your type objects. You can do this by using flatMap.
  2. Get all the unique values for type. In ES6 you can use SET to get all unqiue values in a list
  3. Then you can use filter to get all objects with the same type and subsequently use reduce on only those.

Note: Since count doesn't exist you need to define an initial value for it and since you define count you also have to define the other values you perform a reduce operation on. (No need for id since there since you assign it direclty and the previous value doesn't matter)

I hope you can follow my short explanation.

const newJson = {
        "w_elem": [
            {
                "elem": [
                    {"type": "type_a","price": "100","discount": "10"},
                    {"type": "type_b","price": "120","discount": "12"},
                    {"type": "type_a","price": "165","discount": "10"}
                ],
                "user": "1001",
                "userType": "A"
            },
            {
                "elem": [
                    {"type": "type_a","price": "96.2","discount": "5"},
                    {"type": "type_b","price": "15","discount": "0"}
                ],
                "user": "1011",
                "userType": "B"
            },
            {
                "elem": [
                    {"type": "type_a","price": "96.2","discount": "5"},
                    {"type": "type_b","price": "15","discount": "0"}
                ],
                "user": "1011",
                "userType": "B"
            }
        ]
    }
    
const newArr = newJson["w_elem"]   

/* Get all elem */
const flatElem = newArr.flatMap((item) => {
    return item["elem"]
})

const keys = [... new Set(flatElem.map(item => { return item["type"]}))]


let data = keys.map( id => {
   return flatElem.filter( obj => obj["type"] === id )
    .reduce((acc, cur) => (
        {
        type: id,
        count: acc.count + 1,
        price: acc.price + parseFloat(cur.price), 
        discount: acc.discount + parseFloat(cur.discount)
      }
    ), {count: 0, price: 0, discount: 0})
})

console.log(data)

Solution 2:[2]

You can simplify task by making a flat array of elements. And then group summary values by type.

const data = {"w_elem":[{"elem":[{"type":"type_a","price":"100","discount":"10"},{"type":"type_b","price":"120","discount":"12"},{"type":"type_a","price":"165","discount":"10"}],"user":"1001","userType":"A"},{"elem":[{"type":"type_a","price":"96.2","discount":"5"},{"type":"type_b","price":"15","discount":"0"}],"user":"1011","userType":"B"},{"elem":[{"type":"type_a","price":"96.2","discount":"5"},{"type":"type_b","price":"15","discount":"0"}],"user":"1011","userType":"B"}]};

const elems = data.w_elem.flatMap(({ elem }) => elem);
const result = Object.values(elems.reduce((acc, { type, price, discount }) => {
    acc[type] ??= { type, count: 0, price: 0, discount: 0 };
    acc[type].count += 1;
    acc[type].price += Number(price);
    acc[type].discount += Number(discount);
    
    return acc;
}, {}));

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2