1

I have an array of objects in my code. The objects are having same keys with different or same values.

        var a = [];
        a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
        a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'20.00'});
        a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
        a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'75.00'});
        console.log(a);

The array Looks like below:

enter image description here

I want to iterate through this array and if the taxid is the same in the objects, then the tax_valueshould be summed up.

I tried using two for loops and it's working but in that case, it is checking the id with itself and summing up the value with itself. Like below:

var sum = 0;
var newArray = [];
for (var i=0; i<a.length;i++){
    for (var j=0;j<a.length;j++){
        if(a[i]['taxid']==a[j]['taxid']){
            sum = a[i]['tax_value'] + a[j]['tax_value'];
        }
    }
    newArray.push({taxid : a[i]['taxid'], tax_value : sum});
}

I appreciate your concerns, Thank You.

2
  • 2
    Please post your two for loops. Commented Sep 24, 2017 at 12:12
  • okay.. I'm updating the question Commented Sep 24, 2017 at 12:18

9 Answers 9

4

You can just use reduce and sum the values together

var a = [];
a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'20.00'});
a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'75.00'});

let res = a.reduce((a, b) =>
  a.set(b.taxid, (a.get(b.taxid) || 0) + Number(b.tax_value)), new Map);

console.log(res);

If you want to get the resulting Map to an object, you can use

toObject(map) {
    let obj = Object.create(null);
    for (let [key, value] of map.entries()) {
        obj[key] = value;
    }
    return obj;
} 

console.log(toObject(res));
Sign up to request clarification or add additional context in comments.

Comments

3

you can achive this in different ways.

One ay is to use reduce:

a = a.reduce((c, i)=>{c[i.taxid]=(c[i.taxid]||0)+parseFloat(i.tax_value); return c}, {});

is this is too complecated for you, you can use a loop:

var t = {};
a.forEach((v)=>t[v.taxid]=(t[v.taxid]||0)+parseFloat(v.tax_value));
a = t;

of if the expression is too complecated, you can achive this with a simple if condition:

var t = {};
a.forEach((v)=>{
  if(t[v.taxid])
    t[v.taxid]+=parseFloat(v.tax_value);
  else
    t[v.taxid] = parseFloat(v.tax_value);
});
a = t;

EDIT: In response to the edit of the question and the volitionaly output: [{taxid : NUM, tax_value : NUM}, ...]

a = a.reduce((c, i)=>{
  let cc = c.findIndex((e)=>e.taxid==i.taxid);
  if(cc==-1) c.push({taxid: i.taxid, tax_value: parseFloat(i.tax_value)});
  else c[cc].tax_value += parseFloat(i.tax_value)
  return c
}, []);

2 Comments

Would you pls explain how it works in reduce? Not able to fully understand it
@PranR.V sure. What part you're not understanding? c is the result of the last reduce-callback and i is the current item of the array a. First the callback searches for the index in c where the object with the current taxid is saved. If it doesn't exist push a new object. If it does exist: add the values to the object saved on the found position.
1
var output = a.reduce( (final,data) => {
 let isAlready = final.find( ( value ) => { 
   value.taxid == data.taxid;
 });
 if(!isAlready){
   final.push( data );
 } else {
   var index = final.indexOf(isAlready);
   final[index].tax_value = parseFloat(final[index].tax_value) + parseFloat(data.tax_value);
 }
 return final;
},[] )

1 Comment

changing the values inside the compare callback function is not a good coding style. It's a quick and dirty solution, but is not necessary for such a simple fragment of code. It violates the sense of the find function. No downvote because it works, but I don't like the solution.
1

I think you want something like this as your implementation.

var a = [];
        a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
        a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'20.00'});
        a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
        a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'75.00'});
        
//Sum up all the same taxid tax_value
var temp = {};
a.forEach(function(obj){
   if(!temp[obj.taxid]){
     temp[obj.taxid] = obj.tax_value;
   } else {
     temp[obj.taxid] = Number(temp[obj.taxid]) + Number(obj.tax_value);
   }
});

//Format the data into desired output
var result = [];
for(var key in temp){
  result.push({
    taxid: key,
    tax_value: temp[key]
  })
}

console.log(result)

Comments

1

Cake

const a = [];
a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'20.00'});
a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'75.00'});


const sum = {};
for (var i = 0; i < a.length; i++) {
 const o = a[i];
 const lol = parseFloat(o.tax_value);
 sum[o.taxid] = (sum[o.taxid]) ? sum[o.taxid] + lol : lol;
}
console.log(sum);

Comments

1

Some considerations:

  • tax_value is a string. You need at least an implicit type casting to number, because if you add a number and a string, the number gets converted to string and you get a string concatination, but not an arithmetic operation.

  • Mutate the given data structure or create a new one. In this case and usually always, it is better to generate a new result set instead of using the given structure as data and as result set. This may lead to confusion, because some code may relay on unchanged structures.

Solution

  • Using a hash table, which might be a Map on newer systems (ES6) or an object for keeping reference to the same object with the same taxid.

  • Using an implicid conversion to number with an unary plus +.

  • Using a single loop.

var data = [{ taxid: 1, tax_name: 'VAT', tax_value:' 25.00' }, { taxid: 2, tax_name: 'Service Tax', tax_value: '20.00' }, { taxid: 1, tax_name: 'VAT', tax_value: '25.00' }, { taxid: 2, tax_name: 'Service Tax', tax_value: '75.00' }],
    hash = Object.create(null),
    result = [];

data.forEach(function (o) {
    if (!hash[o.taxid]) {
        hash[o.taxid] = { taxid: o.taxid, tax_name: o.tax_name, tax_value: 0 };
        result.push(hash[o.taxid]);
    }
    hash[o.taxid].tax_value += +o.tax_value;
});

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

Comments

1

**You can do it using forEach loop by creating empty object and start initializing it in each iteration it will check for the taxid if found then it will add to it otherwise it will go to else **

    const a = [];
    a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
    a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'20.00'});
    a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
    a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'7.00'});    
    a.push({taxid : 3, tax_name: 'Service Tax', tax_value:'3.00'});

    let holders={};
    a.forEach(function (item) {
          if (holders.hasOwnProperty(item.taxid)) {
            holders[item.taxid] = holders[item.taxid] + parseFloat(item.tax_value);
          } else {
            holders[item.taxid] = parseFloat(item.tax_value);
          }
        });
        console.log(holders);

Comments

0

This is a possible solution. It involves changing the sum to an object, hence the var sum = {}, and then adding the total of tax_value to any ids that match the other object. I've tried to use your original code, but keep in mind there are more efficient solutions.

PS: the a[i] !== a[j] in the if statement prevents it from checking the 'taxid' with itself

var a = [];
a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'20.00'});
a.push({taxid : 1, tax_name: 'VAT', tax_value:'25.00'});
a.push({taxid : 2, tax_name: 'Service Tax', tax_value:'75.00'});

var sum = {};
var res = 0;
var newArray = [];
for (var i=0; i<a.length;i++){
    for (var j=0;j<a.length;j++){
        if(a[i]['taxid']==a[j]['taxid'] && a[i] !== a[j]){
            sum[a[i]['taxid']] === undefined ? sum[a[i]['taxid']] = 0 : sum[a[i]['taxid']] += parseFloat(a[i]['tax_value']) + parseFloat(a[j]['tax_value']);
        }
    }
}

console.log(sum);

Comments

0

just add another condition to your loop ("if" statement in second loop)

(a[i]['taxid'] === a[j]['taxid'] && j !== i)

explanation
as you said, loop is checking the id with itself, to prevent this you can add "j !== i" so if second loop hits id from first loop it will pass over it so it will not sum itself

2 Comments

I think this condition is not for the loops, but for the if statement. You should also explain why does this modification works.
I thought that it's pretty obvious that should modify if statement in loop, and by loop i meant his solution which is completely valid and require just a bit of polish, anyway thanks for hint, i really should wrote why this is working

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.