Write a Custom JavaScript Filter Function that is 60% faster than Array.filter
Functional Javascript
Posted on August 17, 2020
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!
Posted on August 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 17, 2020