1

My vue application is currently iterating through a data object of employees with hours and swipes, which is being multiplied for each employee every day.

This is working in computing new rows of data so that I have all given data for each day.

However, in my template, I'm trying to set the multipled data to show in the column with the corresponding date and give only one row for each employee. My expected results would be

Employee  |  2021-08-31  |  2021-09-01  |  2021-09-02
-----------------------------------------------------
Evan        60                60
Stan        100               200
Kelly       60                             164

How can I modify what I'm doing so that I can end up with only one row per employee and put the data into the correct column based on the column-header date and the date in the object for that record?

<div id="app">
  <table>
    <thead>
      <tr>
        <th>Employee</th>
        <th>2021-08-31</th>
        <th>2021-09-01</th>
        <th>2021-09-02</th>
        <th>Grand Total</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, index) in compRows">
        <td v-html="row.employee"></td>
        <td v-if="row.workDate == {{$date->addDay()->format($format)}}"></td>
        <td v-html="row.hours"></td>
      </tr>
    </tbody>
  </table>
</div>


@endsection

@section('loadjs')

<script>

new Vue({
  el: "#app",
  data: {
    rows: [
        {  
            employee: "Evan",
            hours: "15",
            workDate: "2021-08-31",
            swipes: "4",
            
        },
        {  
            employee: "Kelly",
            hours: "15",
            workDate: "2021-08-31",
            swipes: "4",
            
        },
        {  
            employee: "Evan",
            hours: "15",
            workDate: "2021-09-01",
            swipes: "4",
           
        },
        {  
            employee: "Stan",
            hours: "25",
            workDate: "2021-08-31",
            swipes: "4",
            
        },
        {  
            employee: "Kelly",
            hours: "82",
            workDate: "2021-09-02",
            swipes: "2",
            
        },
        {  
            employee: "Stan",
            hours: "40",
            workDate: "2021-09-01",
            swipes: "5",
           
        }
    ]
  },
  methods: {

  },
  computed: {
    compRows() {
      const grouped = this.rows.reduce((r, o) => {
        r[o.employee] ??= {};
        r[o.employee][o.workDate] ??= {employee: o.employee, workDate: o.workDate, swipes: 0, hours: 0};
        r[o.employee][o.workDate].swipes += +o.swipes;
        r[o.employee][o.workDate].hours += +o.hours;
        return r;
      }, {});
      return Object.values(grouped).map(o => Object.values(o)).flat();
    }
  }
});


</script>

1 Answer 1

2
  1. Instead of flattening the grouped data in the compRows prop (renamed below as hoursByEmployee), keep it as an object to lookup employee hours by date later. In addition, track a total property that stores the grand total (swipes by hours).

    export default {
      computed: {
        hoursByEmployee() {
          return this.rows.reduce((r, o) => {
            r[o.employee] ??= {}
            r[o.employee][o.workDate] ??= {swipes: 0, hours: 0}
            r[o.employee][o.workDate].swipes += +o.swipes
            r[o.employee][o.workDate].hours += +o.hours
            r[o.employee].total ??= 0 👈
            r[o.employee].total += (+o.swipes * +o.hours) 👈
            return r
          }, {})
        },
      }
    }
    
  2. Use another computed property to get the available dates from the rows' workDate, sorted chronologically:

    export default {
      computed: {
        dates() {
          const dates = [...new Set(this.rows.map(r => r.workDate))]
          return dates.sort((a,b) => new Date(a) - new Date(b))
        },
      }
    }
    
  3. In the template's table header, render the computed dates:

    <thead>
      <tr>
        <th>Employee</th>
        <th v-for="date in dates" :key="date">{{ date }}</th> 👈
        <th>Grand Total</th>
      </tr>
    </thead>
    
  4. In the template's table body, render the computed employee hours by date, using the hoursByEmployee lookup and dates created above. If the hours exist for the date, render the daily total. Also, render the grand total property in the last column.

    <tbody>
      <tr v-for="(hours, employee) in hoursByEmployee" :key="employee">
        <td>{{ employee }}</td>
        <td v-for="date in dates" :key="date">{{ hours[date] && hours[date].swipes * hours[date].hours }}</td> 👈
        <td>{{ hours.total }}</td> 👈
      </tr>
    </tbody>
    

demo

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

1 Comment

That's an incredibly helpful and thorough answer, thank you! Not only does it work, but I immediately saw why I was going in the wrong direction with flattening. This whole time I assumed I would need to flatten but as I got more into it I realized I needed a different approach and this seems to be it. Thanks again!

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.