Javascript: please help me understand this function - javascript

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

Related

How can i target elements in another array via .filter()

Im trying to return the values in argument[0] that are not equal to the values of arguments[1] and so on.
Ive created a variable 'let argArr' that holds the values of the arguments after the first, Im trying to understand why in my .filter() i cannot target 'let argArr'?
function destroyer(arr) {
let argArr = [];
for (let i = 1; i < arguments.length; i++) {
argArr.push(arguments[i])
}
return arr.filter(i => i !== argArr)
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You are comparing an arr array's element to the argArr what is wrong.
The right way is to check if element is inside argArr and filter if yes.
return arr.filter(i => !argArr.includes(i))
Instead of using arguments explicitly specify the array as the first argument, and then use rest parameters to gather all the remaining arguments into an array. Then use filter to return only those elements in the array that are not in the rest array.
function destroyer(arr, ...rest) {
return arr.filter(el => !rest.includes(el));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
Use spread operator to treat arguments as normal array
function foo() {
return [...arguments].filter(el => el);
}
console.log(foo(1,2,3))

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

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.

specifying the value change of a Variable value over time not working

function destroyer(arr) {
var arg = Array.prototype.slice.call(arguments);
arg.splice(0, 1);
return arr.filter(function (val){
return arg.indexOf(val) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
this code is working accurately. But if replace arg.splice(0, 1) with arg = arg.splice(0, 1); , the code doesn't work. Since arg is a variable so its value can be changed over the course of entire code, why it doesn't work like this? Pardon me if this ques looks silly, but i am new to javascript and very curious in exploring every bit of knowledge about it.
Your code not using arg = is correct (though you can do it more efficiently, more below). arg.splice(0, 1) removes the first element from the original array (changing the array in place), and returns a new array containing only the element that was removed. That is:
var a = [1, 2, 3];
console.log(a.splice(0, 1)); // [1]
console.log(a); // [2, 3]
If you use arg = arg.splice(0, 1); you're saying "Remove the first element from arg and replace the arg variable's value with a reference to the new array containing only that removed element. So your subsequent use of arg uses the array containing the removed entry, instead of the array you wanted to use.
You can and probably should avoid splice entirely. Instead, just skip the first argument when copying arguments:
function destroyer(arr) {
var arg = Array.prototype.slice.call(arguments, 1);
// Note --------------------------------------^^^
return arr.filter(function(val) {
return arg.indexOf(val) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);

Removing repeated values from array with multiple arguments

These are the full instructions for what I am trying to accomplish :
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
This is the solution I have:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.slice(0,1);
return arr.filter(function(elements) {
return args.indexOf(element) === -1;
});
}
Keep in mind that there could be any number of arguments (not just 2 or 3).
The solution I have isn't working. What is wrong with my current solution and how can I fix it with an explanation?
Your solution is almost working.
The problems:
args.slice(0,1); does not modify the array (anyway this method just returns an array with the first element). Use args.shift() instead to remove the first element
function(elements) should be function(element) in the filter callback.
A working solution:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.shift();
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
console.log(destroyer([1, 2, 3, 5, 5], 1, 5)); // prints [2, 3]
Check the working demo.

Categories