amu03
amu03

Reputation: 361

Sum values for multiple objects in the array

This is what I have:

const arrayA = [{name:'a', amount: 10, serviceId: '23a', test:'SUCCESS'},
                {name:'a', amount: 9, test:'FAIL'}, 
                {name:'b', amount: 15, serviceId: '23b', test:'SUCCESS'}]

 

(note that there's object not having 'serviceId')

I would like to get:

 [{name:'a', amount: 19, test:'FAIL'},
  {name:'b', amount: 15, test:'SUCCESS'}]

I have searched, and tried something like this: (reference: https://stackoverflow.com/a/50338360/13840216)

const result = Object.values(arrayA.reduce((r, o) => (r[o.name]
  ? (r[o.name].amount += o.amount)
  : (r[o.name] = {...o}), r), {}));

but still not sure how to assign test field.

Any help would be appreciated!

Upvotes: 0

Views: 1928

Answers (5)

Alejandro A.
Alejandro A.

Reputation: 136

That is a nice use case of how to process data in js, particularly how functions like map and reduce can help you do that processing. Which are a nice alternative to loops and iterations.

Moreover, I'd advise you to do the processing in steps, as you have defined in the description. For example:

const arrayA = [
    {name:'a', amount: 10, serviceId: '23a', test:'SUCCESS'},
    {name:'a', amount: 9, test:'FAIL'},
    {name:'b', amount: 15, serviceId: '23b', test:'SUCCESS'}
]

// First group the items by name
const byName = arrayA.reduce((ob, item) => {
    if(!(item.name in  ob))
        ob[item.name] = []
    ob[item.name].push({amount: item.amount, test: item.test})
    return ob
}, {})

// Then compute the total amount in each group and find if there is any FAIL in a group
const sumByName = Object.keys(byName).map(name => { // This is a way to iterate through the groups
    // Sum the amount in all elements of a group
    const amount = byName[name].reduce((sum, item) => sum + item.amount , 0)
    // Find if there is any FAIL in a group
    const test = byName[name].map(item => item.test) // Get an array with only the test string
        .includes('FAIL') ? 'FAIL': 'SUCCESS' // Evaluate if the array includes FAIL
    return ({name, amount, test})
})

console.log(sumByName)

Finally, I'd advise you to watch these videos on map and reduce (and all the content of that channel for this matter)

Upvotes: 1

Abir Taheer
Abir Taheer

Reputation: 2783

Store the sums and final values in an object and then convert them into an array.

const arrayA = [
  { name: "a", amount: 10, serviceId: "23a", test: "SUCCESS" },
  { name: "a", amount: 9, test: "FAIL" },
  { name: "b", amount: 15, serviceId: "23b", test: "SUCCESS" },
];

const map = {};

arrayA.forEach((row) => {
  if (!map[row.name]) {
    map[row.name] = { name: row.name, amount: 0, test: "SUCCESS" };
  }

  map[row.name].amount += row.amount;

  if (row.test === "FAIL") {
    map[row.name].test = "FAIL";
  }
});

const result = Object.values(map);

console.log(result);

Upvotes: 1

Alan Omar
Alan Omar

Reputation: 4217

you can use Array.reduce method:

const arrayA=[{name:"a",amount:10,serviceId:"23a",test:"SUCCESS"},{name:"a",amount:9,test:"FAIL"},{name:"b",amount:15,serviceId:"23b",test:"SUCCESS"}];

let result  = arrayA.reduce((aac,{name,amount,test}) => {
    let idx = aac.findIndex(n => n.name === name)
    if( idx != -1){
        aac[idx].amount += amount;
        if(test === "FAIL")
            aac[idx].test = "FAIL"        
        return aac
    }
    aac.push({name,amount,test})
    return aac
},[])

console.log(result);
console.log(arrayA)

Upvotes: 0

mplungjan
mplungjan

Reputation: 177786

This? Just test if the .test is already fail

const arrayA = [{ name: 'a', amount: 10, serviceId: '23a', test: 'SUCCESS' }, { name: 'a', amount: 9, test: 'FAIL' }, { name: 'b', amount: 15, serviceId: '23b', test: 'SUCCESS' }],
  result = Object.values(
    arrayA.reduce((r, o) => {
      r[o.name] ? (r[o.name].amount += o.amount) : (r[o.name] = { ...o })
      r[o.name].test = r[o.name].test === "FAIL" ? "FAIL" : o.test;
      return r;
    }, {})
  );

console.log(result);

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386560

You need to check test and update the value, if necessary.

const
    array = [{ name: 'a', amount: 10, serviceId: '23a', test: 'SUCCESS' }, { name: 'a', amount: 9, test: 'FAIL' }, { name: 'b', amount: 15, serviceId: '23b', test: 'SUCCESS' }],
    result = Object.values(array.reduce((r, { name, amount, test }) => {
        if (!r[name]) r[name] = { name, amount: 0, test };
        r[name].amount += amount;
        if (test === 'FAIL') r[name].test = 'FAIL';
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 5

Related Questions