I recently realize a POC with Vue.js. I was confronted to this problem: how to apply an unknown number of filter to a collection?

My previous POC works like this: when I click to a filter, this filter is automatically push in an array of filter. And printed datas depends on this array of filters.

Let’s start with the following data:

const heroes = [
  { name: 'Wolverine',      family: 'Marvel',    isEvil: false },
  { name: 'Deadpool',       family: 'Marvel',    isEvil: false },
  { name: 'Magneto',        family: 'Marvel',    isEvil: true  },
  { name: 'Charles Xavier', family: 'Marvel',    isEvil: false },
  { name: 'Batman',         family: 'DC Comics', isEvil: false },
  { name: 'Harley Quinn',   family: 'DC Comics', isEvil: true  },
  { name: 'Legolas',        family: 'Tolkien',   isEvil: false },
  { name: 'Gandalf',        family: 'Tolkien',   isEvil: false },
  { name: 'Saruman',        family: 'Tolkien',   isEvil: true  }
];

And the following filters:

// not DC Comics
const f1 = h => h.family !== 'DC Comics'
// no evil hero
const f2 = h => h.isEvil === false

I can now chain the filters like this:

heroes.filter(f1)
      .filter(f2)
      .filter(whatever)
      // ...

But what if I have to apply severals filters on the same bench of data? … like this:

const activeFilters = [f1, f2]

Looping inside an array can be a solution:

var filteredHeroes = heroes
for (let filterIndex in activeFilters) {
  filteredHeroes = filteredHeroes.filter(activeFilters[filterIndex])
}

But as I explain in my previous post: imperative programming is not very elegant.

Solution: using recursion

A nicer solution is to use a functional approach by using recursion:

function recursive_filter (data, arrayFilters, index = 0) {
  if (arrayFilters.length === 0) {
    return data
  }
  if (index === arrayFilters.length - 1) {
    return data.filter(arrayFilters[index])
  }
  return recursive_filter(data.filter(arrayFilters[index]), arrayFilters, (index + 1))
}
const filteredHeroes = recursive_filter(heroes, activeFilters)

Bad solution: extending native prototype

As explained in the MDN, it is not recommended to extend native object (Array in our case). So the following code will works despite the good practice violation.

// DON't DO IT!
Array.prototype.recursive_filter = function (arrayFilters, index = 0) {
  if (arrayFilters.length === 0) {
    return this
  }
  if (index === arrayFilters.length - 1) {
    return this.filter(arrayFilters[index])
  }
  return this.filter(arrayFilters[index]).recursive_filter(arrayFilters, (index + 1))
}

const filteredHeroes = heroes.recursive_filter(activeFilters)

A la découverte d'Hubot

Je regardais l'autre jour cette conférence d'Alain Hélaïli à propos sur le fonctionnement interne de [Github.com](https://github.com). Au...… Continue reading

Développeur expatrié à Dublin

Published on April 23, 2017

No more for/while loop in JavaScript

Published on March 13, 2017