2

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.

3 Answers 3

2

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/.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

3 Comments

Thanks. I see how the reduce functions work but if I put this logic into the computed functions isn't that going to get called for each iteration of the year when totalOrders and totalRevenue are rendered out (multiple times, once per year loop)? Since those computed values are within the year loop, I was hoping I'd be able to at least contextually get the current year within the computed function so the logic didn't have to reduce all years every time.
Would I be better off using a method rather than a computed function, and passing in the current year in the loop to that method? I'm brand new to Vue so open to advice on the best approach overall.
@Dan I would probably do this as a method with the current year as an argument. You could also cache the totals as a property on the report if you thought you would need to call it more than once. If you went that route, you might even compute the totals once when you receive the data from the api.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.