filter and includes in array, how does that work? - javascript

I'm trying to understand how filter() and includes() work with arrays in javascript but english isn't my native language so I would really appreciate it if someone could explain the example below to me like I was 5:
const removeFromArray = function(...num) {
let array = num[0];
return array.filter(val => !num.includes(val))
};
This function takes an array and some other arguments then removes the other arguments from that array for example removeFromArray([1, 2, 3, 4], 3) should remove 3 and return [1,2,4]
How does this part work?
return array.filter(val => !num.includes(val))
Why the exclamation mark and also how do those two methods work together?

I think the key to understanding what is going on is the parameter(s) of the function, num. The code uses a nice trick that I have not encountered before. So, num is:
[[1, 2, 3, 4], 3];
a 1D array with TWO elements: [1, 2, 3, 4] at index 0, and 3 at index 1. As a result:
num.includes([1, 2, 3, 4]) // is true
num.includes(3) // is true
num.includes(anything-else) // is false
The
Array#includes
method determines whether an array includes a certain value among its
entries, returning true or false as appropriate.
In the simplest form, whenever a boolean expression is prefixed with !, the result of the expression is negated. For example:
!num.includes(3) // becomes false
The
Array#filter
method creates a new array with all elements that pass the test
implemented by the provided function.
Pass the test simply means return true.
Now we are ready to look at num[0].filter(val => !num.includes(val)). Or:
[1, 2, 3, 4].filter(val => !num.includes(val))
Please recall that ONLY 3 and [1, 2, 3, 4] return true to:
num.includes(val)
Hence of all the elements of num[0] or [1, 2, 3, 4] only 3 returns false to the negated expression:
!num.includes(val)
1, 2, and 4 return true or !false, meaning that they pass the test and hence will be returned by the function:
[1, 2, 4];
Please note that val => !num.includes(val) is a shorthand way of writing:
function( val ) {
return !num.includes(val);
}
const removeFromArray = function(...num) {
let array = num[0];
return array.filter(val => !num.includes(val))
};
console.log( removeFromArray([1, 2, 3, 4], 3) );

Rest parameters shouldn't be used like that, it should only be used for like values. So, the array should be accepted separately and only the numbers to remove should be accepted using rest (refer to the snippet below).
The includes() method determines whether an array includes a certain value among its entries, returning true or false as appropriate.
So, we simply filter out numbers that are not present in the itemsToRemove array.
const removeFromArray = (array, ...itemsToRemove) =>
array.filter((item) => !itemsToRemove.includes(item));
removeFromArray([1, 2, 3, 4], 3, 2);

! means "not". If something is falsy (null, 0, false, an empty string), then !something returns true. This leads to a really strange looking "cheat code" where you can convert any value to a boolean (i.e. truthy to true and falsy to false) via !!value. One exclamation point converts it to a boolean value that's true if value is falsy, then the second exclamation point changes true to false (or false to true)!
array.prototype.filter requires a function to be evaluated against each element and returns an array of only the elements where the supplied function returns a truthy value.
It might be easier to think of the following code that is nearly equivalent to yours...
const removeFromArray = function(array, ...valsToRemove) {
const isValToKeep = val => array.includes(val) === false;
return array.filter(isValToKeep)
};
The only difference in this code, besides being longer, is that the first argument won't be looked for within the first argument. Consider
const a1 = [1,2,3];
a1.push(a1); // appends itself as its last element
In your version, removeFromArray(a1, 2) would return [1, 3], but mine doesn't combine the first argument as one of the elements to look for and remove from the first argument, which is probably what most people would expect and be more performant, but would definitely have a different effect in the example returning [1, 3, a1], i.e. [1, 3, [1, 2, 3, [1, 2, 3, [1, 2, 3, [1, 2, 3, [1, 2, 3, [1, 2, 3, [...]]]]]]]]

This is a simple form to explain how to use filter
function isBigEnough(value) {
return value >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// result is [12, 130, 44]
This example is from: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Lets start by rewriting this line:
Before
return array.filter(val => !num.includes(val))
after
const callback = val => {
return !num.includes(val)
}
const filteredArray = array.filter(callback);
return filteredArray
Now lets break and explain parts of this statement:
! num.includes(val)
includes method here will check if num has val in it. If num has val it will return true else it will return false. note the ! at the begining of the line ,that will change the value returned by includes to its opposite e.g if value returned is false it will change it to true if value returned is true it will change it to false.
array.filter(callback)
The filter method here will go through every element of the array and at each element it will ask whether to add that element to the filteredArray or not based on what is returned from the callback. If callback returns true(truthy) it will add it else it will not add it. The callback should only return true(truthy) or false(falsy).
example :
At index 0 of array the filter will ask did callback return true if it returned true the element at index 0 will be added to the filtered array. It will do the same for the rest of the elements.
How they work together:
array has [1,2,3,4] so array.filter(callback) will go over each element and call callback on each. Based on your function num has this [[1,2,3,4],3] so when val from array is 3 !num.includes(val) will return true but this ! will change it to false as a result callback will return false and 3 will not be included in the final array the filteredArray.
Hope a 5 year old can understand this explantion. You can now rewrite your function like this :
const removeFromArray = (array, ...numbersToRemove) => array.filter((number) => !numsToRemove.includes(number));

Related

Using JS every() method

I am trying to test if an array is sorted in ascending order using the JS every method: checking if every element is smaller than the next index.
I don't understand why my code below is not working.
const testArray1 = [1, 2, 3, 4, 5, 6];
const testArray2 = [25, 51, 32, 12, 15];
const isAscending = (element, index, array) => {element < array[index + 1]};
console.log(testArray1.every(isAscending));// expecting true, but getting false
console.log(testArray2.every(isAscending));// expecting false, getting false
I am aware that the sort method exists, this is for a school exercice.
You have two problems.
Your arrow function always returns undefined because it is missing a return statement.
You are comparing the last value in the array to undefined (e.g. when i is 5 for the first array, element is 6 and array[5 + 1] is undefined) which is always false so you need to implement a special case for that entry.

JS - filter v.s. splice in forEach loop

Background knowledge
We can mutate an array using splice and filter. filter is not a mutable method, but splice is a mutable method. So if we use splice to an array, then the original array will be mutated.
The problem
Now, we are going to do forEach to an array and if a condition matches, we will remove an element using splice and filter. Let's see:
1. splice
let arr = [1, 2, 3, 4];
arr.forEach(el => {
console.log(el);
if (el === 2) {
arr.splice(el, 1); // remove element 2
}
});
Yeah that's what we expect. That prints 1, 2, 4. Because in the middle of the loop we mutate the arr, the result is broken.
2. filter
let arr = [1,2,3,4];
arr.forEach(el => {
console.log(el);
if (el === 2) {
arr = arr.filter(el => el !== 2); // remove element 2
}
});
However, as you can see, that prints 1, 2, 3, 4 even if we mutate the arr in the middle of the loop!
The question
We've mutated two arrays in the middle of the loop in a similar way, but the results are different! What happened?
Why does splice affect original array but filter not?
That is simply how Array.prototype.splice, Array.prototype.filter and Array.prototype.forEach are defined. splice is meant to modify the array (and return removed values) while filter is meant to return a copy of the array with only the matching values
Array.prototype.forEach iterates over the array in ascending index order. Even though you modify the array during e.g. index 3, next iteration it'll just go to the next index, e.g. 4, unless the end of the array is already reached. Mind that once .forEach is called on an array, it'll keep working on that array. You setting arr to another value doesn't affect the array nor the state of the .forEach call. Similar to a regular function call:
let someVariable = 5;
function test(param) {
someVariable = 6;
console.log(param); // still prints 5
}
test(someVariable);
After all, JavaScript works by reference (or primitive values), you're not passing a pointer to the someVariable variable like you can do in some other languages.
One point of clarification: in the OP's first example he's actually deleting the third value in the array rather than the second one despite the comment indicating he meant to delete the second element (at least, that is what I think he was going for based on the subsequent example).
One would fix that problem by using the second parameter passed to the forEach callback as follows:
let arr = [1, 2, 3, 4];
arr.forEach((el, index) => {
console.log(el);
if (el === 2) {
// actually remove the second element instead of the element at index 2
arr.splice(index, 1);
}
});
What I find interesting is that even if the semantic error is fixed, the console will still show what the OP mentioned:
1
2
4
This, despite the resulting value of arr being set to [1, 3, 4] by the splice() call.
What happened? The MDN has a similar example regarding modifying an array inside a forEach loop. Basically, the callback one passes to forEach is invoked for every index of the list until it reaches the length of the list. In the second invocation, the underlying logic is pointing to index 1 and the callback deletes that index, moving everything currently following that index forward one index in the array: the value 3 is moved to index 1 and the value 4 is moved to index 2. Because we've already iterated over index 1, the third invocation will be invoked on index 2 which now contains the value 4.
The following table is another way to see this:
Iteration
value of el
arr value before callback
arr value after callback
1
1
[1, 2, 3, 4]
[1, 2, 3, 4]
2
2
[1, 2, 3, 4]
[1, 3, 4]
3
4
[1, 3, 4]
[1, 3, 4]
Basically, you can think of Array.prototype.forEach being defined similarly to the following:
Array.prototype.forEach = function(callbackFn, thisArg) {
for (let index = 0; index < this.length; ++index) {
callbackFn.call(thisArg, this.at(index), index, this)
}
}
The difference between the two examples is that in the first one, the OP is using splice to modify the object referenced by the variable arr "in place" (as noted in the MDN doc). In the second example, the OP is changing the variable arr to point to a new object; however, because forEach is a function, the original object referenced by arr will be kept in scope by the forEach closure until the function completes. This becomes a little easier to see when you add a little more logging to the example using the third parameter passed to the callback (the array against which the callback was executed).
let arr = [1,2,3,4];
arr.forEach((el, index, list) => {
console.log("el:", el, "list:", list, "arr:", arr);
if (el === 2) {
arr = arr.filter(el => el !== 2); // remove element 2
}
});
This modified example will produce the following output:
el: 1 list: [1, 2, 3, 4] arr: [1, 2, 3, 4]
el: 2 list: [1, 2, 3, 4] arr: [1, 2, 3, 4]
el: 3 list: [1, 2, 3, 4] arr: [1, 3, 4]
el: 4 list: [1, 2, 3, 4] arr: [1, 3, 4]
One can see that the value of list, which comes from the forEach closure, never changes despite arr getting overwritten during the second iteration.

Javascript some() returns false on zero

I need to check if numbers exist in my array. Using the some() function I find that zero comes back false. It's a problem because I am working with a ton of different numbers and zero being one of them.
var array = [0, 1, 3, 4, 5];
var test = function(element) {
return 0;
};
console.log(array.some(test));
// expected output: true on 0 <-- getting false
// expected output: true on 1
// expected output: false on 20
In short how can I get 0 to return true?
the test function always returns zero.
var test = function(element) {
return element == 0
};
This way the function should work properly.
The test function should return true/false.
In your case you always return 0 which is evaluated to boolean false.
For what you're trying to implement, it might make more sense to use the .includes function, which tests whether an array includes a value:
var array = [0, 1, 3, 4, 5];
console.log(array.includes(0));
console.log(array.includes(1));
console.log(array.includes(2));
Though, .includes (along with all array iteration methods) is an O(N) process - if you need to carry out a bunch of tests for the same array, you might convert the array to a Set first, so that you can then use Set.has (which is generally O(1)):
var array = [0, 1, 3, 4, 5];
const set = new Set(array);
console.log(set.has(0));
console.log(set.has(1));
console.log(set.has(2));

How does this function works,

Here's the code
var diffArray = function(a, b) {
return b.filter(function(value) { return a.indexOf(value) === -1; });
};
Input is:
diffArray([1, "3", 3, "4"], [1, "1", 3, 4]);
I don't get why does it returns: ["1", 4]
Can you please clarify that to me.
Explaining some things:
Array.indexOf method is a method that searches an array for a particular element and returns the index of the element if it finds it. If it didn't it returns -1.
Array.filter receives a callback function. This callback function is called once for each element of the array and it should return true if the element should be present in the filtered result or false if it shouldn't.
Example: [1, 2, 3].filter(function(value) { return value < 3 }); would return [1, 2].
So you're passing 2 arrays to diffArray. The b array is being filtered and the filter function searches a array for each element of b array and return each element of the b array that is not present in a array (every element that has indexOf === -1);
So the b elements are [1, "1", 3, 4]. Let's look into it step by step:
1 is present in a, so it's not returned because we're looking for the elements NOT present in a
"1" is not present in a so it's returned. a array has 1 element, but that's a different type of "1'
3 is present is a so it's not returned
4 is not present in a so is returned. Again same case of different type as "1".
I hope it's clearer now.

Javascript: please help me understand this function

I've been trying to understand this function for quite a while, but it just doesn't make sense to me. The goal of the function is to remove any numbers within the array of arguments that match the other argument numbers.
Why is it necessary to slice the array for the function to work?
Is args.splice(0,1) redundant? I removed it and nothing changed.
It seems like the filter function does the bulk of the work, but I don't see how it actually filters for the numbers...
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.splice(0, 1);
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Let's go through it line by line:
var args = Array.prototype.slice.call(arguments);
JavaScript's arguments variable is similar to an array but it's not an array. You can try this yourself: arguments instanceof Array will give false. So applying the slice method from the Array prototype will simply convert arguments to a real array.
args.splice(0, 1);
This is to remove the first argument, which is arr in your case.
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
This will go through all the numbers in arr and will check each one of them if it exists in the arguments. When indexOf() returns -1 it means the element was not found in the array.
Slice does not alter. It returns a shallow copy of elements from the original array. Elements of the original array are copied into the returned array.
Take this example
var object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
var sliced = Array.prototype.slice.call( object, 3 );
['three','four']; //output
passe Arguments [Array[6], 2, 3]
Arguments after splicing or remove first element of argument [2, 3]
so closure function filters element which is present in first element of array with other two element. returning just [1, 1]
To understand what's going on, we need to understand the Function.prototype.call method.
It invokes the Array.prototype.slice method on the first argument you pass to it, which in this case is the magical JS arguments object, and then passes in whatever arguments follow.
Thus Array.prototype.splice is unnecessary, and you can just write:
function destroyer(arr) {
var rest = Array.prototype.slice.call(arguments, 1);
return arr.filter(function(element) {
return rest.indexOf(element) === -1;
});
}
in fact, this has been implemented in ES2015+ with the spread operator, so you could write:
function destroyer(arr, ...rest) {
return arr.filter(function(element) {
return rest.indexOf(element) === -1;
});
}
I've added comments, please see if it helps you to understand the function.
function destroyer(arr) {
// arr just holds [1, 2, 3, 1, 2, 3]
var args = Array.prototype.slice.call(arguments);
// args contains nested array with all input params [[1, 2, 3, 1, 2, 3], 2, 3]
args.splice(0, 1);
//args is spliced and we have [2,3] in args
//Filter arr=[1, 2, 3, 1, 2, 3] elements, condition it must not be in args i.e [2,3]
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Please refer to the below documentation to read about arguments object used in this function:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments
In arr we will have [1,2,3,1,2,3] and
in args we will have [[1,2,3,1,2,3],2,3]
The filter function loop over arr
and args.indexOf(element) will return -1 if element is not in args.
So, for the first time in loop the element value is 1 and inside loop
args.indexOf(1) returns -1 because 1 is not present in args because at 0 index we have array and at 1st index we have 2 and at 2nd index we have 3. So the condition === -1 is true and returns 1 to the array that is going to be printed to the console.
for next element, i.e., 2 in arr, the statement args.indexOf(2) returns the first index at which 2 is present i.e., 1 in args array. for likewise entire loop will be executed for arr

Categories