So I wanted the elements of the array arr1
that also happen to belong to the array arr2
. I figured arr1.filter(arr2.includes)
should do the trick, but it gave me an error (see below). Strangely, though, arr1.filter(x => arr2.incudes(x))
worked fine. Even though the functions arr2.includes
and x => arr2.includes(x)
aren't referentially equal, shouldn't they take the same values on the same inputs? What am I missing, here?
> arr1 = ['a', 'b', 'c']
[ 'a', 'b', 'c' ]
> arr2 = ['a', 'c', 'd']
[ 'a', 'c', 'd' ]
>
> arr1.filter(x => arr2.includes(x))
[ 'a', 'c' ]
> arr1.filter(arr2.includes)
TypeError: Cannot convert undefined or null to object
at includes (<anonymous>)
at Array.filter (native)
at repl:1:6
... etc ...
There are two reasons you can't just do arr1.filter(arr2.includes)
:
arr2.includes
is just a reference to the function, but what you need is both a reference to the function and to the array that you want to use it on (arr2
). You could solve that by using Function.prototype.bind
, but:
filter
passes its callback multiple arguments, not just one: It passes the value, its index, and the original array. includes
will try to use the second argument it receives as the index at which to start searching, so when filter
passes it the index, it'll use that and skip leading entries.
So the usual solution is to use a wrapper function that knows it needs to use includes
on arr2
and knows to only pass it the one argument — which is what you've done with your arrow function.
But see also Michał Perłakowski's answer for an answer from the functional programming perspective using a utility function to create the callback function rather than creating it inline.
Here's how you could implement an includes
function that could be used in point-free style:
const arr1 = ['a', 'b', 'c'];
const arr2 = ['a', 'c', 'd'];
const includes = arr => x => arr.includes(x);
console.log(arr1.filter(includes(arr2)));
If you're interested in functional programming in JavaScript, you should try the Ramda library. With Ramda your code could look like this:
const arr1 = ['a', 'b', 'c'];
const arr2 = ['a', 'c', 'd'];
// First option: R.flip
console.log(R.filter(R.flip(R.contains)(arr1), arr2));
// Second option: R.__ (placeholder argument)
console.log(R.filter(R.contains(R.__, arr1), arr2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With