8

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?

4 Answers 4

11

@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>
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you. I have found this easier to understand.
The anwer have syntax error, computed routine missing a ')'.
i need this as well in vue 3. gonna try it later.
5

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>

Comments

2

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.

1 Comment

You can import single lodash function. Using computed is also 'at least' two loops, one on receive data from API response, one on any CRUD method performed.
1

You could take advantage of computed property as follow, (run this code snippet to see) :

var app = new Vue({
  el: "#app",
  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"
}]
  },
  computed: {
      groupedMakes() {
          var makes={};
          this.cars.forEach((item)=>{
         if(makes[item.make]==undefined){
              makes[item.make]=[];
              makes[item.make].push(item.model)
                }
          else{
            makes[item.make].push(item.model);

            }
           });
         
           return makes;
      }
  }
})
<div id="app">
    <div v-for="(make,index) in groupedMakes">
        <h3>{{index}}</h3>
         <ul>
            <li v-for="model in make">
            {{model}}
            </li>
         </ul> 
    </div>
   
</div>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

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.