Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to group data from attributes using for loops in VueJS

Tags:

vue.js

I'm trying to work out how to group data at two levels using for loops in VueJS

As an example I have a VueJS app with the following data:

cars: [{
  "make": "Ford",
  "model": "Mustang"
}, {
  "make": "Ford",
  "model": "Thunderbird"
}, {
  "make": "Ford",
  "model": "Fairlane"
}, {
  "make": "Chevrolet",
  "model": "Camaro"
}, {
  "make": "Chevrolet",
  "model": "Chevelle"
}, {
  "make": "Plymouth",
  "model": "Barracuda"
}]

It's straightforward to loop through the cars using a for loop:

<div v-for="car in cars">
  <div>Make: {{ car.make }}</div>
  <div>Model: {{ car.model }}</div>  
</div>

But what if I wanted to group the models by make? The output I'm after is:

Ford
  Mustang
  Thunderbird
  Fairlane
Chevrolet
  Camaro
  Chevelle
Plymouth
  Barracuda

What's the best way to do this in VueJS?

like image 883
BaronGrivet Avatar asked Sep 23 '18 21:09

BaronGrivet


3 Answers

@DigitalDrifter has a fine solution, but you might find the following easier to read. It is less efficient, though, so I definitely wouldn't use it for large collections (e.g. thousands of cars).

computed: {
    makes() {
        const makes = new Set();
        this.cars.forEach(car => makes.add(car.make);
        return Array.from(makes); 
    }
},
methods: {
    models(make) {
        return this.cars
            .filter(car => car.make === make)
            .map(car => car.model);
    }
}

and in the template

<div v-for="(make, index) in makes" :key="index">
    <p>Make: {{ make }}</p>
    <ul>
        <li v-for="(model, innerIndex) in models(make)" :key="innerIndex">
            {{ model }}
        </li>
    </ul>
</div>
like image 121
Stephen Thomas Avatar answered Nov 16 '22 16:11

Stephen Thomas


You can use reduce in a computed property:

computed: {
  groups () {
    return this.cars.reduce((carry, current) => {
      if (carry.hasOwnProperty(current.make) && Array.isArray(carry[current.make])) {
        carry[current.make].push(current.model)
      } else {
        Object.assign(carry, { [current.make]: [current.model] })
      }
      return carry
    }, {})
  }
}

The template can loop the groups like:

<div v-for="(make, index) in Object.keys(groups)" :key="index">
  <p>Make: {{ make }}</p>
  <ul>
  <li v-for="(model, innerIndex) in groups[make]" :key="innerIndex">
    {{ model }}
  </li>
  </ul>
</div>
like image 25
Brian Lee Avatar answered Nov 16 '22 17:11

Brian Lee


Why waste so much cpu on computed rather than doing some data massage that have a structure which is ui friendly?

props: { 
  cars: Array 
},
data() {
  return {
    makes: _.groupBy(this.cars, 'make'); // _.groupBy is a lodash function
  };
},
methods: {
  // some CRUD methods
  add() {},
  update() {},
  delete() {},
  reconstruct() {
    var result = [];
    Object.keys(this.makes).forEach(key => {
      result.push(...this.makes[key]);
    });
    return result;
  }
}

<div v-for="(make, key) in makes" :key="key">
<p>Make: {{ make }}</p>
<ul>
    <li v-for="(car, index) in make" :key="index">
        {{ car.model }}
    </li>
</ul>
</div>

You only needs to construct/deconstruct when talking to APIs.

like image 2
Mengo Avatar answered Nov 16 '22 16:11

Mengo