Search code examples
javascriptfunctional-programming

Functional use of Array.includes causes TypeError


I would expect [1].some([1].includes) to return true, instead I get an error saying:

Uncaught TypeError: Cannot convert undefined or null to object.

Any ideas what would cause this? As I understand, Array.some accepts a function invoked per array item, which [1].includes should fulfill.


Solution

  • You lose the context when you pass that function as is: when it's invoked as a callback, value of this inside it is undefined in strict mode (and global object in non-strict mode), not [1]. To address this issue, you may fix the context:

    [1].some([].includes.bind([1]))
    

    Note that it doesn't matter which array is used to access includes function; you might as well write that as...

    [1].some( Array.prototype.includes.bind([1]) )
    

    That's be a bit less concise, but a bit more efficient (as no immediate array is created). Still, it almost never should be a bottleneck; thus you should better optimize for readability.


    Unfortunately, this won't be enough. See, Array.includes() uses two parameters:

    arr.includes(searchElement[, fromIndex])
    

    ... and Array.some() does supply it with those two (even three in fact, but only two are used by includes). That's why this...

    [1,2,3].some([].includes.bind([1])); // true
    

    ... works, but this...

    [2,1,3].some([].includes.bind([1])); // false
    

    ... doesn't: the lookups in [1] array start from 0th, 1st, 2nd elements - and apparently fail after the first one.

    To fix this, you might either create a function that takes exactly one argument with something like lodash's _.unary:

    [2,1,3].some(_.unary([].includes.bind([1]))) // now we're talking!
    

    ... or bite the bullet and use arrow function instead. Note that you can still use a function with a bound context here:

    const checker = [].includes.bind([1]);
    [2,1,3].some(el => checker(el));
    

    ... to make this more flexible.