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
CodePudding user response:
You were close.
- Seems like
actionsCount
is always an object, never an array - 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)
CodePudding user response:
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>
CodePudding user response:
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);