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)

Immutability in JavaScript (without library)

Last month, I gave a talk about [*Immutability for functional JavaScript* in the DublinJS meetup (slides)](http://slides.maxpou.fr/immuta...… Continue reading

A la découverte d'Hubot

Published on July 14, 2017