Write a Custom JavaScript Filter Function that is 60% faster than Array.filter

Functional Javascript - Aug 17 '20 - - Dev Community

Here is a simple rewrite of a javascript filter func...

/**
@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...

//@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).

/**
@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...

//@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...

// invocation of this pipe must receive data of type "object[]",
// - otherwise it throws
pipeArrOfObjs
// 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.

// 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!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player