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

52 Views Asked by At

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
  }, {}))
}]
2

There are 2 best solutions below

5
Nina Scholz On BEST ANSWER

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; }

0
Chris G On

Start with reduce function to iterate thru input.data.

for each object get the id.

initialize state and city properties as Set objects to ensure uniqueness.

if the id is already present in the accumulator, then update its properties.

after the reduction is complete, convert each state and city set back to an array using the spread operator ([...set]).

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
      },
    },
  ],
};

let result = Object.values(input.data.reduce((acc, curr) => {
  let id = curr.metadata.id;
  if (!acc[id]) {
    acc[id] = { ...curr
    };
    acc[id].state = new Set([curr.state]);
    acc[id].city = new Set([...curr.city]);
  } else {
    acc[id].state.add(curr.state);
    acc[id].tests += curr.tests;
    curr.city.forEach(city => acc[id].city.add(city));
  }
  return acc;
}, {}));

result.forEach(obj => {
  obj.state = [...obj.state];
  obj.city = [...obj.city];
});

result = {
  "data": result
};

console.log(result);