ƒ(m)(o)(b)(b).dev

      

How to make simple filtration with JavaScript

How to make simple filtration with JavaScript

I will try to explain how I abstracted filtration functionality to easily refine list of items. I am using JavaScript .reduce(), .filter() and .every() methods in simple symbiosis in order to apply multiple filtrations on a list.

I am a big fan of abstracting and reusing functionality in order to follow the DRY (Don't Repeat Yourself) principle. Whenever I can I take the time to figure out if the implementation I came up with can be abstracted in reusable bits which can be composed in different ways so you can use it in different variations. I am hugely influenced by Functional Programming principles and the way they are explained by Eric Elliott so I try to use currying, function composition, and higher-order functions whenever possible. As I am learning new things I try to use them asap so I can test them, so this article is an attempt to make filtration utility using the same principles.

My most recent project is a middle-sized React Native application. The single unit at its core is an Event. A simplified version of this Event object is something like this:

{
 name: "Event 1",
 description: "Event 1 description",
 isSavedToMyProfile: true,
 isTop: true,
 endingAt: "2020-02-01",
}


The main screen is a list of these items, which must have a filtrating functionality with 3 filters - "Saved", "Top", "Closing soon". According to specs, the filtration must be implemented with checkboxes and the user should be able to combine them. So I thought it would be nice to abstract this behaviour so I can reuse it in other projects.

So let's see some code

/**
* Filtration utility around Array.prototype.filter
* arrFilter :: [a] -> (a -> bool) -> [a]
*/
const arrFilter = arr => f => arr.filter(item => f(item));


Here is a simple utility in which I can pass the list and apply predicate function on the item.

/**
* Function to check if item passes selected filter
* checkItem :: [a] -> (b -> bool) -> [bool]
*/
const checkItemAgainstFilter = options => checks => item => {
 const filtrations = options.map(option) => [...acc, checks(option, item)], []);

 return filtrations.every(filtration => filtration === true);
};


With this utility I take the options array, a custom function which takes the option and the item and make the checking and the item. This constructs an array of booleans (for each option) and .every() returns true if every value is true which means that the item passes every filter.

So now let's see how to use this.

// 1. List of items to filter
const items = [
 {
   name: 'Book',
   isTop: true,
   isSaved: false,
   hurry: true,
 },
 {
   name: 'Shirt',
   isTop: false,
   isSaved: true,
   hurry: true,
 },
 {
   name: 'Monitor',
   isTop: true,
   isSaved: false,
   hurry: false,
 },
 {
   name: 'Phone',
   isTop: false,
   isSaved: false,
   hurry: false,
 },
 {
   name: 'Bag',
   isTop: true,
   isSaved: true,
   hurry: true,
 },
];
// 2. Options array
const myOptions = [
 {
   value: 'Top',
   isSelected: false,
 },
 {
   value: 'Saved',
   isSelected: false,
 },
 {
   value: 'Last Chance',
   isSelected: true,
 },
];
// 3. Define your checks - simple switch will work
const checkMyItem = (option, item) => {
 if (option.isSelected) {
   switch (option.value) {
     case 'Top':
       return item.isTop;
     case 'Saved':
       return item.isSaved;
     case 'Last Chance':
       return item.hurry;
     default:
       return false;
   }
 }

 return true;
};
// 4. Apply filtration
const filteredItems = arrFilter(items)(checkItemAgainstFilter(myOptions)(checkMyItem));
filteredItems.map(item => console.log(item.name));


The first two points are obvious - we need list of items and options array. The checks are simple switch and there you should provide your testing logic.

I hope this is somehow helpful at least in terms of function composition, currying and javascript array methods.

Powered by