Write a Custom JavaScript Filter Function that is 60% faster than Array.filter
Tags: javascript, functional, performance, filter
Published: 2020-08-17 17:47:25

Here is a simple rewrite of a javascript filter func...
```JS
/**
@func util
a custom high-performance filter

@perf
60% faster than the built-in JavaScript filter func

@typedef {(e: *) => boolean} filterFnAny
@param {filterFnAny} fn
@param {*[]} a
@return {*[]}
*/
const fil = (fn, a) => {
  const f = []; //final
  for (let i = 0; i < a.length; i++) {
    if (fn(a[i])) {
      f.push(a[i]);
    }
  }
  return f;
};
```

Here is a sample test to show how this func is used...
```JS
//@tests
const aNums = [1, 2, 3, 4, 5, 6, 7, 8];
log(fil(e => e < 5, aNums)); // [1, 2, 3, 4]
```

From this we create a runtime-enforced strongly-typed variant.
(We curry-ify it so we can use this func in our strongly-typed functional pipelines (See the sample usages below).
```JS
/**
@func curry
filter an arr of objs

@typedef {(e: object) => boolean} filterFnObjs
@param {filterFnObjs} fn
@return {(a: object[]) => object[]}
*/
export const filterObjs = fn => a => throwIfNotArrOfObjs(a) || fil(fn, a);
```


Here are three different idomatic usages of this strongly-typed filter...
```JS
//@tests
const objs = [{ n: 15 }, { n: 2 }];

//a.
log(filterObjs(o => o.n > 3)(objs)); // [ { n: 15 } ]

//b.
const p1 = pipeArrOfObjs(
  filterObjs(o => o.n > 3), lArr, // [ { n: 15 } ]
);
p1(objs);

//c.
const p2 = pipeNil(
  () => objs,
  filterObjs(o => o.n > 3), lArr, // [ { n: 15 } ]
);
p2();
```


# Stongly-Typed Functional Pipeline Notes:

## 1.
Two runtime-enforced strongly-type pipe funcs indicate what type of data must be passed into the start of the pipe...

```JS
// invocation of this pipe must receive data of type "object[]",
// - otherwise it throws
pipeArrOfObjs
```


```JS
// invocation of this pipe must receive no arguments
// - otherwise it throws
pipeNil
```

## 2.
Funcs that begin with an "l" indicate a log func.
The "l()" func can receive any type, which will be logged.
"lArr()" must receive an arr, otherwise it throws.

## 3.
Test example "c." is what's called a "closure pipe", meaning it accepts data from it's outer scope, in this case via a lambda (anonymous function), the "objs" data is injected into the pipe, "() => objs".

Closure pipes are very powerful and flexible, as you can inject outside data at any point within the piping process.

## 4.
The JSDoc syntax informs the development-time experience of type issues, and is also used by the TypeScript background compiler in VSC (Visual Studio Code) to infer and inform on type issues.



# Performance Gains:

Here are the results of running each function independently, comparing the performance difference between the built-in js filter func and the custom-built loop-based one.
```JS
// test a: one iteration on large array
// loop wins by being 62% faster
const aNums = genNums(10e6);
timeInLoop("Array.filter", 1, () => aNums.filter(n => n < 10e6)); //Array.filter: 1e+0: 1.460s
timeInLoop("fil", 1, () => fil(n => n < 10e6, aNums)); // fil: 1e+0: 896.562ms

// test b: large iteration on small array
// loop wins by being 9% faster
const aNums = [1, 2, 3, 4, 5, 6, 7, 8];
timeInLoop("Array.filter", 10e6, () => aNums.filter(n => n < 8)); //Array.filter: 1e+7: 1.748s
timeInLoop("fil", 10e6, () => fil(n => n < 8, aNums)); //fil: 1e+7: 1.601s
```

# timeInLoop (performance test func) Source Code:
https://gist.github.com/funfunction/91b5876a5f562e1e352aed0fcabc3858

# Contact:

More real world examples coming up in the future.

Feel free to subscribe if you'd like to see more Javascript-based runtime-enforced strongly-typed functional pipelining.

And leave a comment if you have any questions or input.
Or tweet me or DM me at
https://twitter.com/reactivizer

See you soon!