Search code examples
javascriptarraysloopsobjectiteration

sum up property values when id matches and return the result using javascript


I am trying to achieve a pretty complex iteration but havent been successful so far using reduce. Here is a small example of it

The input is as follows

let input = {
    "data": [
        {
            "state": "CA",
            "tests": 12,
            "city": [ "Mountain View"],
            "metadata": { "id": 15 },
        },
        {
            "state": "TX",
            "tests": 10,
            "city": [ "Austin" ],
            "metadata": { "id": 15 },
        },
        {
            "state": "AZ",
            "tests": 5,
            "city": [ "Flagstaff" ],
            "metadata": { "id": 10 },
        },
    ],
}

As you can clearly see, the id is repeated twice in 2 objects. i want to concatenate these two objects into one when id is same. so if there are 3 objects with same id, i want to concatenate 3 objects as one. Apart from this, i also want to concatenate the state an city property when the id is matched. and the tests should add up too when the id is matched.

So my final output should look like this

let result = {
    "data": [
        {
            "state": ["CA", "TX"],
            "tests": 22,
            "city": ["Mountain View", "Austin"],
            "metadata": { "id": 15 },
        },
        {
            "state": "AZ",
            "tests": 5,
            "city": [ "Flagstaff" ],
            "metadata": { "id": 10 },
        },
    ],
}

can someone please let me know how to precisely achieve this. Here is how i have tried using reduce and i have reached half way. the only thing i am unable to get is how do i sum up tests property when id is matched and get the tests total to be 22 in this case.

let total_tests = 0;
let res = [{
  data: Object.values(input[0].data.reduce((acc, {state, city, metadata, tests, category
  }) => {
    const id = metadata ? .id;

    if (!acc[id]) {
      acc[id] = {
        id,
        state: [],
        city: [],
        metadata,
        tests,
        category
      }
    }
    acc[id].state.push(state)
    acc[id].city.push(...city)
    debugger
    acc[id].tests = total_tests + acc[id].tests;
    return acc
  }, {}))
}]

Solution

  • You could initialise the object with

    tests: 0
    

    and add the actual value to the object/property with the same id:

    acc[id].tests += tests;
    

    const
        input = { data: [{ state: "CA", tests: 12, city: ["Mountain View"], metadata: { id: 15 } }, { state: "TX", tests: 10, city: ["Austin"], metadata: { id: 15 } }, { state: "AZ", tests: 5, city: ["Flagstaff"], metadata: { id: 10 } }] },
        result = [{
            data: Object.values(input.data.reduce((acc, { state, city, metadata, tests }) => {
                const id = metadata?.id;
                acc[id] = acc[id] || { id, state: [], city: [], metadata, tests: 0 };
                acc[id].state.push(state);
                acc[id].city.push(...city);
                acc[id].tests += tests;
                return acc;
            }, {}))
        }];
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }