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>
Related
I am currently trying to bring the elements in an array into other arrays in the array. I have now read that .map works well and have found newArrayA as an example. Can't I just leave out the dots and just do it as in newArrayB? Does anyone have anything else to read? I can find almost nothing on this example. Is there anything that speaks against newArrayB?
let test = ['A', 'B', 'C', 'D']; // to [ [ 'A' ], [ 'B' ], [ 'C' ], [ 'D' ] ]
newArrayA = test.map((x) => [...x]);
newArrayB = test.map((x) => [x]);
console.log(newArrayA);
console.log(newArrayB);
You can see the difference by increasing the length of one of the strings - you get different results when you use the spread operator vs simply using the value at an index - just run this example:
let test = ['ABCDE', 'B'];
newArrayA = test.map((x) => [...x]);
newArrayB = test.map((x) => [x]);
console.log(newArrayA);
console.log(newArrayB);
I like to use Array.prototype.reduce() for several different scenarios in my code, it's pretty straightforward and handy.
Please observe in the 2 different solutions below the reduce() function takes 2 different initial values, in the first case is new Set() and the second is [].
In the below example the code uses reduce() without return keyword - Set one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.add(c), new Set());
console.log(Array.from(result));
The next example is using still reduce() but here with a return keyword - Array one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => {
a.find(e => e === c) ? null : a.push(c);
return a;
}, []);
console.log(result);
Question:
So the .add() function for Set returns the Set object itself. The .push() function for Array returns the length of the used Array.
The Set case helps me to shorten the code using .reduce() without return keyword because the above mentioned reason. In my second example I would like to use the solution without return keyword but still with Array somehow.
Is there any workaround solution to get the same result but without using return keyword in the second example? I would like to shorten the code further if possible.
Any help is appreciated.
You could take either the accumulator, if found or concat the element to the accumulator.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.find(e => e === c) ? a : a.concat(c), []);
console.log(result);
Just to mention, Set takes a complete array with the constructor.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = Array.from(new Set(data));
console.log(result);
Array#concat can add a new item to an array and returns a new array, so can works similar to Set#add. However, it still needs the conditional operator since you want to either add an element or nothing - for the latter case that's concatenating an array with an empty array:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.concat(a.some(e => e === c) ? [] : c), []);
console.log(result);
Alternatively, you can use spread syntax to again combine two arrays:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => [...a, ...(a.some(e => e === c) ? [] : c)], []);
console.log(result);
Neither of the two is perfect, to be honest. The existence of the conditional operator makes this harder to read when all one line but it's still an option.
I want to get the remainder of an iterable like an array. Like so:
var arr = ['a', 'b', 'c'];
var brr = arr.slice(1);
But is there a terser way (maybe using destructuring assignment?).
Yes there is:
var [,...brr] = arr; // ['b', 'c']
Multiple elision also works:
var [,,...brr] = arr; // ['c']
must I check B is in which index? How can I get rid of B? says I have a function that receive a param that might be A,B,C
let grade = ['A','B','C']
delete grade['B']; // this won't work?
console.log(grade)
If you're really committed to the idea of using the delete operator on an array, you could it as so:
let grade = ['A', 'B', 'C'];
delete grade[grade.indexOf('B')];
Note, however, that this does not accomplish what it is that you likely want to do. More clearly, I assume you'd want the operation above to return ['A', 'C']. It actually does not. Rather, you get an undefined at index 1 (where the value B previously resided).
console.log(grade);
['A', undefined x 1, 'C']
The most appropriate operation to properly displace the B from the array would be to use Array#splice. For example:
let grade = ['A', 'B', 'C'];
grade.splice(grade.indexOf('B'), 1);
console.log(grade);
['A', 'C']
I have an array of string and want to instantly remove some of them. But it doesn't work
var list = ['a', 'b', 'c', 'd']
_.remove(list, 'b');
console.log(list); // 'b' still there
I guess it happened because _.remove function accept string as second argument and considers that is property name. How to make lodash do an equality check in this case?
One more option for you is to use _.pull, which unlike _.without, does not create a copy of the array, but only modifies it instead:
_.pull(list, 'b'); // ['a', 'c', 'd']
Reference: https://lodash.com/docs#pull
As Giuseppe Pes points out, _.remove is expecting a function. A more direct way to do what you want is to use _.without instead, which does take elements to remove directly.
_.without(['a','b','c','d'], 'b'); //['a','c','d']
Function _.remove doesn't accept a string as second argument but a predicate function which is called for each value in the array. If the function returns true the value is removed from the array.
Lodas doc: https://lodash.com/docs#remove
Removes all elements from array that predicate returns truthy for and
returns an array of the removed elements. The predicate is bound to
thisArg and invoked with three arguments: (value, index, array).
So, if you want to remove b from your array you should something like this:
var list = ['a', 'b', 'c', 'd']
_.remove(list, function(v) { return v === 'b'; });
["a", "c", "d"]