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?
@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>
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With