Search code examples
javascriptarraysobjectlodashreduce

Javascript: Merge Objects in array with different key/value pairs, and getitng the sum from the same keys


So what I have is an array of objects

[
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 5,
            "assign to specialist": 1
        },
        "changesTotal": 6
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 13,
            "duplicate": 5,
            "return for changes": 1
        },
        "changesTotal": 19
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 5
        },
        "changesTotal": 5
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 11,
            "return for changes": 1,
            "assign to specialist": 1
        },
        "changesTotal": 13
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 7
        },
        "changesTotal": 7
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 6
        },
        "changesTotal": 6
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 5,
            "assign to specialist": 1
        },
        "changesTotal": 6
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "assign to specialist": 2,
            "edit record": 17
        },
        "changesTotal": 19
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 2,
            "assign to specialist": 2
        },
        "changesTotal": 4
    },
    {
        "email": "[email protected]",
        "actionsCount": {
            "edit record": 17,
            "duplicate": 2
        },
        "changesTotal": 19
    }
]

As you can see there will be multiple entries under the same email, these correspond to each month that user did some actions, as such new entries will populate each month, some most of them by the same users. I don't know all of the actions, but I can be assured that the actions will all be spelled the same. The data is old so as you can see someone used spaces for the key which is another task for another day.

I want to combine objects by the same email, then sum the same actions across the months, and add actions that are present in one month but not another.

So as an example [email protected] end result would look like

Would look like

[{
  "email": "[email protected]",
  "actionsCount": {
      "edit record": 16,
      "return for changes": 1,
      "assign to specialist": 1
  }
  "changesTotal": 18
}]

So because edit record is present in both occurrences of actionsCount they are simply added up, and return for changes and assign to specialist are present in one but not the other

Right now I feel like I'm close to what I'm trying to achieve? My current approach is as follows

tier2CollectorsActions.reduce((acc, {email, actionsCount}) => {
          acc[email] ??= {email: email, actionsCount: []};
          if(Array.isArray(actionsCount))
            acc[email].actionsCount = _.merge(acc[email].actionsCount, actionsCount)
          else
            acc[email].actionsCount.push(actionsCount);
          return acc;
        }, {})

Which gives me back

{
    "email": "[email protected]",
    "actionsCount": [
        {
            "edit record": 5,
            "assign to specialist": 1
        },
        {
            "assign to specialist": 2,
            "edit record": 17
        }
    ]
}

As you can see each actionsCount array corresponds to the two separate [email protected] object's actionsCount data. I need the actionsCount data back not as an array but an object, I'm doing it as an array in the example bc its so far been the only way I can get the data from both actionCounts. I've been trying to use Object.value and reduce but I haven't quite gotten yet but I feel like I'm so close.

Any direction/tips/suggestions would be much appreciated


Solution

  • You were close.

    1. Seems like actionsCount is always an object, never an array
    2. Just add the things to the object

    const data = [{
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 5,
          "assign to specialist": 1
        },
        "changesTotal": 6
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 13,
          "duplicate": 5,
          "return for changes": 1
        },
        "changesTotal": 19
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 5
        },
        "changesTotal": 5
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 11,
          "return for changes": 1,
          "assign to specialist": 1
        },
        "changesTotal": 13
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 7
        },
        "changesTotal": 7
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 6
        },
        "changesTotal": 6
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 5,
          "assign to specialist": 1
        },
        "changesTotal": 6
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "assign to specialist": 2,
          "edit record": 17
        },
        "changesTotal": 19
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 2,
          "assign to specialist": 2
        },
        "changesTotal": 4
      },
      {
        "email": "[email protected]",
        "actionsCount": {
          "edit record": 17,
          "duplicate": 2
        },
        "changesTotal": 19
      }
    ]
    
    const result = data.reduce((acc, {
      email,
      actionsCount,
      changesTotal
    }) => {
      acc[email] ??= {
        email: email,
        actionsCount: {},
        changesTotal: 0
      };
      Object.entries(actionsCount).forEach(([key, value]) => {
        acc[email].actionsCount[key] ??= 0
        acc[email].actionsCount[key] += value
      })
      acc[email].changesTotal += changesTotal
      return acc;
    }, {})
    
    console.log(result)