0

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

2
  • does _.merge know how to sum the values? I don't think so. Commented Oct 10, 2022 at 21:24
  • Why are you setting actionsCount to an array? You don't want an array in your expected result, it's just an object. Commented Oct 10, 2022 at 21:26

3 Answers 3

1

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)

Sign up to request clarification or add additional context in comments.

1 Comment

Wow I feel utterly dumb I sat here for an embarrasing amount of time wondering why it wasn't working. I tried Object.value/map combo earlier but couldn't connect the dots. Thank you
1

Use lodash's _.groupBy() to group the items by email, and then map the groups and merge them using _.mergeWith(). In the customizer function of _.mergeWith() check if either of the values is a number (since one of them might be undefined), and if they are numbers sum them. If not, let _.mergeWith() handle them by return undefined:

const { flow, groupBy, map, mergeWith, isNumber } = _

const fn = flow(
  arr => groupBy(arr, 'email'),
  groups => map(groups, items => mergeWith(...items, (o1, o2) =>
    isNumber(o1) || isNumber(o2) ? (o1 ?? 0) + (o2 ?? 0) : undefined 
  ))
)

const arr = [{"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 = fn(arr)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

3 Comments

Ah I was looking into lodash's _.mergeWith function, so thank you for demonstrating it's usage. I've been getting more into using lodash (wasn't something I particularly used much, but I see it's immense value). If I'm understanding _.mergeWith correctly, it's like _.merge but you can create a function that it will utilise?
You can add a customizer function to control the merge between two values.
Gotcha, I'll need to mess with lodash more, it's been proving to be an amazing library. Not sure how I've gone this long without it.
0

You shouldn't initialize actionsCount to an array. That's causing it to push each actionsCount object onto the array, instead of combining the properties.

You need to loop over the properties in actionsCount, adding them to the corresponding value in acc[email].

const tier2CollectorsActions = [{
    "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 = tier2CollectorsActions.reduce((acc, {
  email,
  actionsCount
}) => {
  acc[email] ??= {
    email: email,
    actionsCount: {}
  };
  Object.entries(actionsCount).forEach(([key, value]) => {
    acc[email].actionsCount[key] = (acc[email].actionsCount[key] || 0) + value;
  });
  return acc;
}, {})

console.log(result);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.