loading...

Array filter

jamesrweb profile image James Robb Updated on ・3 min read

The ability to filter data to a subset of itself is an important thing to understand when you are a software engineer, data scientist or otherwise working with data in some form. In this article we will take a look at how we may create our own implementation of the native filter functionality that is available in some form in most common languages. In our case the language of choice will be JavaScript.

In most implementations the filter function will take a predicate to test each item in the collection and if the predicate is true, that item will be added to the new filtered collection. As an example, in vanilla JavaScript we could do the following:

const candidates = [{
  name: "James",
  age: 26
}, {
  name: "Dave",
  age: 21
}, {
  name: "Sally",
  age: 15
}, {
  name: "Marc"
}];

function candidateAgeFilterFn(candidate) {
  return candidate.age && candidate.age >= 16;
}

const eligableForDrivingTest = candidates.filter(candidateAgeFilterFn);
console.log(eligableForDrivingTest); // [ { name: 'James', age: 26 }, { name: 'Dave', age: 21 } ]

Our aim is to implement a custom filter function to replicate this behaviour.

Tests

describe('filter', () => {
  it('should apply the condition correctly', () => {
    const collection = [-1, 2, -3];
    const filterFn = item => item > 0;
    const actual = filter(collection, filterFn);
    const result = [2];
    expect(actual).toStrictEqual(result);
  });
});

Generally we just need to test that given a collection and a predicate, the subset is returned as expected. Just like our article about array map, filter is a generally simple implementation to achieve as we will see later in the next section of this article and thus this test is enough for now to use as a proof.

Implementation

The native filter function has the following signature:

let new_array = arr.filter(function callback(currentValue[, index[, array]]) {
    // return element for new_array
}[, thisArg])

We will aim to reproduce this behaviour with the following implementation:

/**
 * @function filter
 * @description A function to filter a collection via a filtering function
 * @param {Array} collection - The collection to filter
 * @param {Function} filterFn - When this function returns true, the item is added to the final output collection
 * @returns {Array} The filtered collection
 */
function filter(collection, filterFn) {
  const output = [];
  const clone = [...collection];
  for (let index = 0; index < clone.length; index++) {
    const item = clone[index];
    const condition = filterFn(item, index, clone);
    if (condition === true) {
      output.push(item);
    }
  }
  return output;
}

We instantiate two arrays in the function body, the first will be our output array and the second is a clone of the collection array. As with our article about array map we clone the collection since we will pass this clone into the provided filterFn and if the user decides to alter the array reference, the initial collection will not have mutated, only the clone. Next we loop each item in the cloned collection and run the filterFn, being sure to pass in the item, index and cloned array to match the native implementation. Finally we check if the filterFn returns true and if so, we add the current item to the output array. Once every item has been looped over and filtered we return the output.

Using our example of the native implementation near the top of this article, we could do the following to achieve the same results:

const candidates = [{
  name: "James",
  age: 26
}, {
  name: "Dave",
  age: 21
}, {
  name: "Sally",
  age: 15
}, {
  name: "Marc"
}];

function filter(collection, filterFn) {
  const output = [];
  const clone = [...collection];
  for (let index = 0; index < clone.length; index++) {
    const item = clone[index];
    const condition = filterFn(item, index, clone);
    if (condition === true) {
      output.push(item);
    }
  }
  return output;
}

function candidateAgeFilterFn(candidate) {
  return candidate.age && candidate.age >= 16;
}

const eligableForDrivingTest = filter(candidates, candidateAgeFilterFn);
console.log(eligableForDrivingTest); // [ { name: 'James', age: 26 }, { name: 'Dave', age: 21 } ]

Conclusions

Hopefully this article gave you some insight into how the native filter function works in languages like JavaScript. PHP uses array_filter(collection, filterFn), Python uses filter(filterFn, collection), etc. You can see the similarities of these and so with your new understanding of the mechanics at play, go and experiment and see what you can make happen. Re-invent the wheel and gain a deeper understanding of your tools and it'll help you going forward with your craft.

Posted on by:

jamesrweb profile

James Robb

@jamesrweb

I am a Polyglot Software Engineer focussed on the web πŸ§‘πŸ»β€πŸ’», an ardent Accessibility Advocate β™Ώ, amateur creative coder πŸ§‘πŸ»β€πŸŽ¨ and an aspiring teacher πŸ‘¨πŸ»β€πŸ«

Discussion

pic
Editor guide
 

Note that condition === true is different from Array.prototype.filter() which checks for Truthy value

 

Note that that’s intentional.