Code Snippet Series: Remove Junk from Array
Functional Javascript

Functional Javascript @functional_js

About: Full Stack System Designer and Software Engineer

Location:
Vancouver
Joined:
Jan 2, 2019

Code Snippet Series: Remove Junk from Array

Publish Date: Aug 2 '20
27 6

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

Comments 6 total

  • BeasTea
    BeasTeaAug 2, 2020

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

    • Functional Javascript
      Functional JavascriptAug 2, 2020

      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
      
      • BeasTea
        BeasTeaAug 4, 2020

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

  • pentacular
    pentacularAug 2, 2020

    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)
    
  • Functional Javascript
    Functional JavascriptAug 2, 2020

    Quiz Hint:
    How many loops are there?

  • HoKangInfo
    HoKangInfoApr 21, 2021

    I guess,

    1. v == null
    2. v is Array
    3. v is String but test v.length === 0 , then trim
    4. v is Object
Add comment