3

I am using a computed property to filter through an array of values and pull out only a set of unique items from within the array. Originally I only needed it for one variable but I need to use it to pull two different values now and can't get it to work independently of each other when making two computed properties.

Here's my Vue HTML code:

<div v-for="item in getUniques">
<input :value="item.car" v-model="selectedCars" type="checkbox">
<label>&nbsp;{{item.car}}</label>
</div>

Here's the function:

 data(){
      return {

selectedCars: [],
prefetch: [
           {car: "XC90", brand: "Volvo"},
           {car: "XC60", brand: "Volvo"},
           {car: "XC90", brand: "Volvo"},
           {car: "X-Type", brand: "Jaguar"}   
          ]
 }
},


computed: {

getUniques(){

                return this.prefetch.reduce((seed, current) => {

                    return Object.assign(seed, {
                        [current.car]: current
                    });

                }, {});

            },

}


// This works perfectly for pulling out the unique values of cars and returns... [XC90,XC60, X-Type] 

But when I try to create another computed property to do the same, but only work with brands, everything breaks and I get undefined. How can I tweak this computed property so I can filter out all the other unique values in this array?

I've tried everything and can't work it out. Thanks in advance for any help!!

al3xs
  • 29
  • 4
  • so replacing `[current.car]` with `[current.brand]` gives you an error? – Flame Dec 19 '19 at 20:13
  • it doesn't if I run it independently, but when I try to run them both at the same time as computed it throws an error. I know it's not recommended to add logic into computed properties, so I am stuck at figuring out where to go from there – al3xs Dec 19 '19 at 20:15
  • I am interested though as to why this happens. I dont believe `.reduce()`does anything odd with the `prefetch` object but apparently it breaks when multiple computed properties use the same object? – Flame Dec 19 '19 at 20:20
  • Correct. Switching over to a map function as recommended by @Daniel below actually did the trick in order to run several computed properties at the same time. I would still love to find out if there's a way to stream along a single computed property rather than have to maintain several single map functions as the size of the sample array grows – al3xs Dec 19 '19 at 20:28
  • Computed properties cache their output. They create a dependency list based on the content, and will only re-compute if a perceived dependency has changed. Two things you can try - 1) make the unique property more visible. This may turn out to be a bit hacky. 2) return a function which takes the unique property name, in this case the function is cached but works properly each time it's called. This may be done more simply as a method, rather than a computed property. – Richard Matsen Dec 19 '19 at 20:50

2 Answers2

4

For the basic check for uniques

For an array like:

let prefetch = [
  {car: "XC90", brand: "Volvo"},
  {car: "XC60", brand: "Volvo"},
  {car: "XC90", brand: "Volvo"},
  {car: "X-Type", brand: "Jaguar"}   
 ]

You could use 2 the filter function as stated here: Get all unique values in a JavaScript array (remove duplicates)

let prefetch = [
  {car: "XC90", brand: "Volvo"},
  {car: "XC60", brand: "Volvo"},
  {car: "XC90", brand: "Volvo"},
  {car: "X-Type", brand: "Jaguar"}   
 ]

 function getUniqueCars() {
   return prefetch.map(x => x.car).filter((v,i,s) => s.indexOf(v) === i)
 }

 function getUniqueBrands() {
  return prefetch.map(x => x.brand).filter((v,i,s) => s.indexOf(v) === i)
 }

 console.log(getUniqueCars())
 console.log(getUniqueBrands())

This also works as a computed function within vue...

A solution for vue using a temporary Array

Another solution could be the creation of a temporary Array to calculcate the unique car/brand pairs you want to use...

let vm = new Vue({
  el: '#app',
  data() {
    return {
      selectedCars: [],
      prefetch: [{
          car: "XC90",
          brand: "Volvo"
        },
        {
          car: "XC60",
          brand: "Volvo"
        },
        {
          car: "XC60",
          brand: "Volvo"
        },
        {
          car: "XC90",
          brand: "Volvo"
        },
        {
          car: "X-Type",
          brand: "Jaguar"
        }
      ]
    }
  },
  computed: {
    getUniques() {
      let tempArray = [];
      for (let item of this.prefetch) {
        (tempItem => {
          if (!(tempItem.length > 0 && tempItem.find(x => x.brand === item.brand))) tempArray.push(item);
        })(tempArray.filter(x => x.car === item.car))
      }
      return tempArray;
    }
  }
})
<div id="app">
  <div v-for="item in getUniques">
    <input :value="item.car" v-model="selectedCars" type="checkbox">
    <label>&nbsp;{{item.brand}} {{item.car}}</label>
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Community
  • 1
  • 1
Daniel
  • 141
  • 6
  • Thanks! this works. However, I would still like to find out if there is a way to reduce the amount of functions by making everything into a single computed property since the array I'm working with is quite large – al3xs Dec 19 '19 at 20:30
  • I (without refactoring) would build a tempArray to calculate uniques like ````function getUniques() { let tempArray = []; for(let item of prefetch) { (tempItem => { if(!(tempItem.length > 0 && tempItem.find(x => x.brand === item.brand))) tempArray.push(item); })(tempArray.filter(x => x.car === item.car)) } return tempArray; }```` – Daniel Dec 19 '19 at 20:44
  • This last comment might work ok as a single computed - why don't you add it to the answer body for a bit more clarity, and show how the template uses it? – Richard Matsen Dec 19 '19 at 21:18
  • @RichardMatsen Thanks for the advice. I've added another solution using the temporary Array. – Daniel Dec 19 '19 at 21:38
0

you can use lodash's uniq

and do

return _.uniqBy(this.prefetch, 'car');
Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
  • See [Computed Caching vs Methods](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods), this may not help. – Richard Matsen Dec 19 '19 at 20:53
  • @RichardMatsen unless I'm misundertanding his question, the cached version should be fine? I'm still a little unclear on what his uniqueness criteria is though – Keith Nicholas Dec 19 '19 at 20:56
  • 1
    Indeed, I'm confused about the final pattern al2xs wants to see. Note he needs the use `car` and `brand` plus other criteria in the future. The unique value must be seen as a **reactive dependency**, so a string literal does not qualify. Setting `car` in a separate data property would do the trick, but it's a bit noisy and difficult to implement in the template. A method is a better way, since methods do not cache. – Richard Matsen Dec 19 '19 at 21:03