Jim Dover
Jim Dover

Reputation: 623

JavaScript sum of values through object

I'm returning JSON data from an API. I am then using map to create my own object.

The data has multiple objects. Here is an example:

{
  "data": {
    "job": [
      {
        "jobNumber": "1",
        "jobTasks": {
          "jobTask": [
            {
              "total": { "amountString": "10.00" }
            },
            {
              "total": { "amountString": "10.00" }
            }
          ]
        }
      },
      {
        "jobNumber": "2",
        "jobTasks": {
          "jobTask": [
            {
              "total": { "amountString": "20.00" }
            },
            {
              "total": { "amountString": "20.00" }
            }
          ]
        }
      },
      {
        "jobNumber": "3",
        "jobTasks": {
          "jobTask": [
            {
              "total": { "amountString": "30.00" }
            },
            {
              "total": { "amountString": "30.00" }
            }
          ]
        }
      }
    ]
  }
}

As from that returned example, I want to have the following:

jobNumber: 1: Total 20. jobNumber: 2: Total 40. jobNumber: 3: Total 60.

Here is the code I am using:

var array = response.data.map(function(item, array) {

   var sumTaskTotal = item.jobTasks.reduce(function(sumTaskTotal, item, index) {
       if (item !== null && item.total !== null) {
          sumTaskTotal += item.total.amountString;
          return sumTaskTotal;
       }
    }, 0);

    array = response.data.map(item => ({
      jobNumber: item.jobNumber,
      sumTaskTotal: sumTaskTotal,
    }));
}

However the sum I am using just keeps summing, so I end up with:

jobNumber: 1: Total 120. jobNumber: 120: Total 120. jobNumber: 3: Total 120.

I have tried for loops but that doesn't seem to get me anywhere.

Upvotes: 1

Views: 75

Answers (2)

Fazal Rasel
Fazal Rasel

Reputation: 4526

you can use just forEach to get your desired string.

const data = {
  "data": {
    "job": [
      --
    ]
  }
}

let string = "";

data.data.job.forEach((item) => {
  const sum = item.jobTasks.jobTask.reduce((total, i) => parseFloat(i.total.amountString) + total, 0);
  string += `jobNumber: ${item.jobNumber}: Total ${sum}. `;  
});
console.log(string);

jsbin

there will be a unnecessary space after your string. use trim function if necessary.

Upvotes: 0

dork
dork

Reputation: 4568

Not actually sure how you got jobNumber: 1: Total 120. jobNumber: 120: Total 120. jobNumber: 3: Total 120.. I tried your code and when you log the array, you'd just get an array with 3 undefined items; each iteration in the map in your code doesn't return anything that's why the array contains 3 undefined items.

Also, data and jobTasks are objects. You should use map or reduce on the job and jobTask properties.

As for adding the totals, amountString is a string (obviously), and you should convert it into a number first. Otherwise, the result in your first iteration would be 010.00 since you're concatenating a string to a number. It's not adding 0 and 10.00.

The array = response.data.map... part in your outer map would just go through all the jobs again. Each iteration in map expects an item to be returned. If you don't return anything, undefined will the value of that array. So instead of mapping the array again, you could just do this:

return {
  jobNumber: item.jobNumber,
  sumTaskTotal,
};

That would then result in an array with 3 objects:

[{
  jobNumber: '1',
  sumTaskTotal: 20,
}, {
  jobNumber: '2',
  sumTaskTotal: 40,
}, {
  jobNumber: '3',
  sumTaskTotal: 60,
}]

But, you can do a nested reduce instead:

const formatted = response.data.job.reduce((prevString, currentJob) => {
  const total = currentJob.jobTasks.jobTask.reduce((prevTotal, currentJobTask) => {
    return prevTotal + Number(currentJobTask.total.amountString);
  }, 0);

  return `${prevString}jobNumber: ${currentJob.jobNumber}: Total ${total}. `;
}, '');

const response = {
  "data": {
    "job": [{
      "jobNumber": "1",
      "jobTasks": {
        "jobTask": [{
          "total": {
            "amountString": "10.00"
          }
        }, {
          "total": {
            "amountString": "10.00"
          }
        }]
      }
    }, {
      "jobNumber": "2",
      "jobTasks": {
        "jobTask": [{
          "total": {
            "amountString": "20.00"
          }
        }, {
          "total": {
            "amountString": "20.00"
          }
        }]
      }
    }, {
      "jobNumber": "3",
      "jobTasks": {
        "jobTask": [{
          "total": {
            "amountString": "30.00"
          }
        }, {
          "total": {
            "amountString": "30.00"
          }
        }]
      }
    }]
  }
};

const formatted = response.data.job.reduce((prevString, currentJob) => {
	const total = currentJob.jobTasks.jobTask.reduce((prevTotal, currentJobTask) => {
  	return prevTotal + Number(currentJobTask.total.amountString);
  }, 0);
  
  return `${prevString}jobNumber: ${currentJob.jobNumber}: Total ${total}. `;
}, '');

console.log(formatted.trim());

Upvotes: 1

Related Questions