Search code examples
vue.jsvuejs2v-for

Removing duplicates from a v-for list in Vue


I'm trying to sort my list of notes by category. I have multiple notes in a single category, so v-for returns the assigned note category multiples times in the list.

I understand that I should use a computed property to filter the list, but I've experimented with the one below, sortedCategories, and can't seem to get it working.

Perhaps also relevant, I'm using Vue2 filters to sort the list alphabetically currently. After I get the list working without duplicates the next step is to be able click on the category and pull up all the notes in that particular category.

My code is:

<template>
  <div class="notebook">
    <nav class="navbar" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
      </div>
    </nav>
    <ul>
      <!--
      <li v-for="(page, index) of pages" class="page" v-bind:class="{ 'active': index === activePage }"  @click="changePage(index)" v-bind:key="index">
        <div>{{page.category}}</div>
      </li>
      -->
      <li v-for="(page, index) of orderBy(pages, 'category')" class="page"
        v-bind:class="{ 'active': index === activePage }" v-bind:key="index">
        <div>{{ page.category }}</div>
      </li>
      <li class="new-page">Add Page +</li>

    </ul>
  </div>
</template>

<script>
  import Vue from 'vue'
  import Vue2Filters from 'vue2-filters'
  Vue.use(Vue2Filters)

  export default {
    name: 'Notebook',
    props: ['pages', 'activePage'],
    mixins: [Vue2Filters.mixin],
    computed: {
      sortedCategories() {
        return this.categories.filter(function (category, position) {
          return this.pages.indexOf(category) == position;
        });
      }
    },
    methods: {
      changePage(index) {
        this.$emit('change-page', index)
      },
      newPage() {
        this.$emit('new-page')
      },
    }
  }
</script>

Solution

  • If you just want to display a sorted list of unique category strings, you could easily do this with vanilla JavaScript:

    1. Use Array.reduce() on pages[] to group the array entries by category, where duplicate categories would overwrite the previous ones (thereby removing duplicates).
    2. Use Object.values() on result #1 to get the array entries.
    3. Use Array.sort() on result #2 to sort the array by category.

    new Vue({
      el: '#app',
      data: () => ({
        pages: [
          { id: 1, category: 'Technology' },
          { id: 2, category: 'Sports' },
          { id: 3, category: 'Business' },
          { id: 4, category: 'Health' },
          { id: 5, category: 'Technology' },
          { id: 6, category: 'Health' },
          { id: 7, category: 'Technology' },
        ]
      }),
      computed: {
        sortedCategories() {
          const cats = this.pages.reduce((p,c) => {
            p[c.category] = c
            return p
          }, {})
          return Object.values(cats).sort((a,b) => a.category.localeCompare(b.category))
        }
      }
    })
    <script src="https://unpkg.com/vue@2.6.12"></script>
    
    <div id="app">
      <ul>
        <li v-for="cat of sortedCategories" :key="cat.id">
          <div>{{ cat.category }}</div>
        </li>
      </ul>
    </div>