DEV Community

Functional Javascript
Functional Javascript

Posted on • Edited on

Remove null and undefined from array in JavaScript Code Snippet Series: Remove Junk from Array

Quiz

There are two key areas to increase the performance of this code without losing robustness; can you spot them?

/** @func remove junk (non-value-based data) from an arr @param {*[]} a @return {*[]} */ export const removeNonVals = a => a.filter(v => { return v !== null && v !== undefined && !(v.constructor === String && v.trim() === "") && !(v.constructor === Object && Object.keys(v).length === 0) && !(Array.isArray(v) && v.length === 0) }); //@tests //@fixtureData const aDirty = [ "Casbah", "abcd", "", "p", "", "255", undefined, null, "", " ", [], {}, "[]", "{}" [[]], [["nested"]], NaN, Infinity, 0, -0, 5, -1, 1e30, BigInt(3145), n => n , , , , , ]; const aClean = removeNonVals(aDirty); console.log(aClean); /* @output [ 'Casbah', 'abcd', 'p', '255', 'Warsaw', '1855', '[]', [ [ 'nested' ] ], NaN, Infinity, 0, -0, 5, -1, 1e+30, 3145n, [Function (anonymous)] ] */ //@perftest timeInLoop("removeNonVals", 1e6, () => removeNonVals(aDirty)) /* removeNonVals: 1e+6: 2.601s */ 

TimeInLoop Source Code

https://gist.github.com/funfunction/91b5876a5f562e1e352aed0fcabc3858

Top comments (6)

Collapse
 
pentacular profile image
pentacular

I suggest that you start by defining a predicate to determine if thing is junk or not.

const isJunk = (thing) => ...; 

And since you want to filter on the inverse.

const isNotJunk = (thing) => !isJunk(thing); 

Now, you have something meaningful to use with things like filter.

stuff.filter(isNotJunk) 
Collapse
 
beastea3 profile image
BeasTea

Do not know why, I changed the condition from v.constructor to v typeof and get a better performance.

Collapse
 
functional_js profile image
Functional Javascript • Edited

Good catch BeasTea,

I've replicated your findings.
It's impressively about 4 TIMES faster.

Interestingly, if you compare the v.constructor and v typeof independently, performance-wise they are effectively the same.

The reason I've preferred v.constructor over typeof is because of its robustness factor; the v typeof returns false for explicit instantiations of types, eg. "new String()"

So what's happening here is a compiler optimization for typeof; where the compiler fails to optimize for the v.constructor.

This would actually be a bug, or at least an optimization deficiency, in the V8 compiler.

Because of the vagaries of compilers, we must perf-test our code and hunt out performance bottlenecks.

//@perftest //a. typeof const isEmptyStr = s => typeof s === "string" && s.trim() === ""; timeInLoop("isEmptyStr", 100e6, () => isEmptyStr("asdf")) //isEmptyStr: 1e+8: 1.019s //b. s.constructor const isEmptyStr2 = s => s !== null && s !== undefined && s.constructor === String && s.trim() === ""; timeInLoop("isEmptyStr2", 100e6, () => isEmptyStr2("asdf")) ////isEmptyStr2: 1e+8: 1.038s 
Collapse
 
beastea3 profile image
BeasTea

Thx for ur explanation, that's impressive!!!

Collapse
 
hokanginfo profile image
HoKangInfo

I guess,

  1. v == null
  2. v is Array
  3. v is String but test v.length === 0 , then trim
  4. v is Object
Collapse
 
functional_js profile image
Functional Javascript

Quiz Hint:
How many loops are there?