Dan
Dan

Reputation: 5996

Sum values in nested loop in vuejs

I'm learning vuejs and am implementing a report system, taking some data from a mock API and outputting it into a table. The data returns monthly order numbers and values for multiple years. An example of the data is here: https://api.myjson.com/bins/u5gp6.

What I want to do is loop through each year and month and output the number of orders and order values, with a sum per year.

The HTML is like this:

<div id="app">
  <div v-for="report in reports">
    <h2>{{ report.year }}</h2>
    <table class="table table-striped">
      <thead>
        <tr>
          <th>Month</th>
          <th>Number of orders</th>
          <th>Total revenue</th>
          <th>Average order</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="value in report.values">
          <td>{{ value.month }} {{ value.year }}</td>
          <td>{{ value.orders }}</td>
          <td>£{{ value.revenue }}</td>
          <td>£{{ value.average }}</td>
        </tr>
      </tbody>
      <tfoot v-if="reports">
        <tr>
          <td>Total {{report.year }}</td>
          <td>{{ totalOrders }}</td>
          <td>£{{ totalRevenue }}</td>
          <td></td>
        </tr>
      </tfoot>
    </table>
  </div>
</div>

The JS is like this:

const app = new Vue({
  el: '#app',
  data: {
    reports: []
  },
  created() {
    fetch('https://api.myjson.com/bins/u5gp6')
      .then(response => response.json())
      .then(json => {
      this.reports = json.reports
    });
  },
  computed: {
    totalOrders: function () {

    },
    totalRevenue: function () {

    }
  }
});

A fiddle can be seen here: https://jsfiddle.net/eywraw8t/63295/

The part I'm struggling with is calculating the totalOrders and totalRevenue values for each year.

I've tried various things like adding a reduce function in the computed total functions but just can't get anything to work. I think I'm getting confused by the fact this is a nested loop.

Can anyone suggest how to approach this such that totalOrders and totalRevenue are populated correctly?

Many thanks.

Upvotes: 2

Views: 4760

Answers (3)

Pri Nce
Pri Nce

Reputation: 721

You can try this.

var array = [
  {name: "Peter", age: 43},
  {name: "John", age: 32},
  {name: "Jake", age: 21}
];

array.reduce(function(sum, current) {
  return sum + current.age;
}, 0); // 43 + 32 + 21 = 96

Upvotes: 0

Dan
Dan

Reputation: 5996

I found that using methods and passing the values object of the current year in the loop through as a parameter, I was able to call the reduce method just on the orders and revenues in that particular year, without having to loop through everything each time. The resultant working code is…

HTML:

<div id="app">
  <div v-for="report in reports">
    <h2>{{ report.year }}</h2>
    <table class="table table-striped">
      <thead>
        <tr>
          <th>Month</th>
          <th>Number of orders</th>
          <th>Total revenue</th>
          <th>Average order</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="value in report.values">
          <td>{{ value.month }} {{ value.year }}</td>
          <td>{{ value.orders }}</td>
          <td>£{{ value.revenue }}</td>
          <td>£{{ value.average }}</td>
        </tr>
      </tbody>
      <tfoot v-if="reports">
        <tr>
          <td>Total {{report.year }}</td>
          <td>{{ totalOrders(report.values) }}</td>
          <td>£{{ totalRevenue(report.values) }}</td>
          <td></td>
        </tr>
      </tfoot>
    </table>
  </div>
</div>

JS:

const app = new Vue({
  el: '#app',
  data: {
    reports: []
  },
  created() {
    fetch('https://api.myjson.com/bins/16731e')
      .then(response => response.json())
      .then(json => {
      this.reports = json.reports
    });
  },
  methods: {
    totalOrders: function (values) {
      return values.reduce((acc, val) => {
        return acc + parseInt(val.orders);
      }, 0);    
    },
    totalRevenue: function (values) {
      return values.reduce((acc, val) => {
        return acc + parseInt(val.revenue);
      }, 0);
    }
  }
});

Working fiddle: https://jsfiddle.net/4js8L3p9/.

Upvotes: 2

Mark
Mark

Reputation: 92461

You can get a report of sales and orders broken out by year with reduce on the original array and then calling reduce on each year's values.

For example:

let reports = [{"year":"2018","values":[{"month":"Jan","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Feb","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Mar","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Apr","orders":"5","revenue":"50.00","average":"10.00"},{"month":"May","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Jun","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Jul","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Aug","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Sep","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Oct","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Nov","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Dec","orders":"5","revenue":"50.00","average":"10.00"}]},{"year":"2017","values":[{"month":"Jan","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Feb","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Mar","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Apr","orders":"5","revenue":"50.00","average":"10.00"},{"month":"May","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Jun","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Jul","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Aug","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Sep","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Oct","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Nov","orders":"5","revenue":"50.00","average":"10.00"},{"month":"Dec","orders":"5","revenue":"50.00","average":"10.00"}]}]

function totalRevenue(){
    return reports.reduce((obj, year) => {
      obj[year.year] = year.values.reduce((total, month) => {
        return total + parseInt(month.revenue)
      }, 0)
      return obj
    }, {})
  }
  

console.log(totalRevenue())

You can do the same thing with orders by replacing month.revenue with month.orders. You could also let the functions take a year argument and then just report that year, but it probably makes sense to only loop through once if you will be reporting every year on the page.

Upvotes: 1

Related Questions