How does this function works, - javascript

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.

Related

filter and includes in array, how does that work?

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));

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 Exercise: Remove all the array's elements before the given one

I'm making some practice with Javascript. This exercise from js.checkio.org asks this:
Not all of the elements are important. What you need to do here is to remove from the array all of the elements before the given one. We have two edge cases here: (1) if a cutting element cannot be found, then the array shouldn't be changed. (2) if the array is empty, then it should remain empty.
Here is my code:
function removeAllBefore(array, number) {
if (array.length === 0)
return array;
for (let element of array)
if (element === number)
filtered = array.filter(n => n => number);
return filtered;
}
console.log(removeAllBefore([1, 2, 3, 4, 5], 2));
I am able to have the new array's values, the problem is with the statement where "if (element !== number) return array", I can't put it in the code without errors and now I am stuck and curious about the solution.
Thanks for all the support
Carlo
(2) is a bit misleading as special case, as an empty array won't contain the element, and thus returning it unchanged as (1) requires is already what (2) asks for.
Otherwise learn about indexOf() and slice() for example:
function removeAllBefore(array, number) {
var position = array.indexOf(number);
if(position === -1) // not found
return array;
return array.slice(position);
}
console.log(JSON.stringify(removeAllBefore([1, 2, 3, 4, 5], 2))); // original test
console.log(JSON.stringify(removeAllBefore([1, 3, 4, 5], 2))); // "unchanged" test
console.log(JSON.stringify(removeAllBefore([], 2))); // empty test
(JSON.stringify() is added only to show the arrays in one line - they would be printed in a one-element-per-line format otherwise)
function removeAllBefore (array, number) {
const indexOfElement = array.indexOf(number);
if(indexOfElement !== -1) {
return array.splice(indexOfElement, array.length - 1);
}
return array;
}
console.log(removeAllBefore([1, 2, 3, 4, 5], 6)); // element not found
console.log(removeAllBefore([1, 2, 3, 4, 5], 3)); // found element
console.log(removeAllBefore([], 6)); // empty array
First find the index of the element, then just use .splice() to remove elements before this index.
function removeAllBefore(array, number) {
let filtered = [];
if (array.length === 0) return array;
for (let element of array)
if (element === number)
array.splice(0, array.indexOf(element))
filtered = array
return filtered;
}
console.log(removeAllBefore([1, 2, 3, 4, 5], 2));
This removes all values preceding the one you have defined, and leaves the rest including the one you have defined
The splice function takes 2 arguments, the index you are removing from and the index before the last element to remove: splice(//from beginning of array, //to index before last element to remove) ...
And the indexOf function gets the index of the value you have defined

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

I need some clarification on the program below. How does the "t" argument affect the program

The code works, I just have no idea how the "t" value becomes the number 4 in the second array. Any help will be greatly appreciated.
function diff(arr1, arr2) {
var newArr = arr1.concat(arr2);
function checkit(t) {
if (arr1.indexOf(t) === -1 || arr2.indexOf(t) === -1) {
return t;
}
}
return newArr.filter(checkit);
}
diff([1, 2, 3, 5], [1, 2, 3, 4, 5]);
Array filter() method creates a new array with all elements that pass the test implemented by the provided function in its argument.
In your case you are providing the checkit method to filter where it iterates through newArr. For each iteration t is the value present in newArr where you are basically checking whether t is present in both of arr1 and arr2 or not. If either of them do not have that element, you are returning back the element.
Here 4 is the value.
To make it more clear, I will break this into steps :
var newArr = arr1.concat(arr2);
Here after concat newArr becomes [1, 2, 3, 5, 1, 2, 3, 4, 5].
In the next step newArr.filter() method is called.
As per the filter() docs : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter filter() method filters out the elements from the invoked array that passes the required test as per the callback provided. Here the callback or the method is checkit().
In the checkit() method the t represents the value of an element in newArr. So in the if condition you are checking whether t is present in arr1 and arr2 or not.
Let's take the 1st element of newArr as an example. Now 1st element in newArr is 1. arr1.indexOf(1) would give you zero, because element 1 is present at the zeroth postion of arr1. Same happens with arr2. Now arr1.indexOf(1) === -1 would give you false and arr2.indexOf(1) === -1 would also give you false.
It will go on like this until you reach value 4 ie. the second last element in newArr. Here when you do arr1.indexOf(4), you will get -1 as the element 4 is not present in the arr1. So arr1.indexOf(1) === -1 would give you true. The if condition will get true and the current element t ie 4 will be returned.
Next as 5 is present in both the arrays the if condition won't get satisfied. Hence you are left with only 4 in your array.

Categories