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

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.

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.

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.

Check if every element in array has a specfic value

I have an array with integers in it; and a function that returns a string. In this example, the function returns either 'solved' or 'unsolved', based on the fact if a specific task was done before or not.
Now I need a way to call my function with every array element as parameter and check if the function returns 'solved' for every element.
Small example with real values:
var array1 = [2,5,8]; // 2 is solved, 5 is solved, 8 is unsolved
var array2 = [2,5,6]; // every element is solved
if (Function returns solved for every element) {
// array2 would be accepted for this part
}
else {
// array1 would go in this branch
}
Array.prototype.every()
The every() method tests whether all elements in the array pass the test implemented by the provided function.
If you already have a function that returns a string (i.e. 'solved' or 'unsolved'), then you can simply convert that to a boolean inside the callback you supply to .every().
var array1 = [2, 5, 8]; // 2 is solved, 5 is solved, 8 is unsolved
var array2 = [2, 5, 6]; // every element is solved
function isSolvedString(operand) {
return operand < 8 ? 'solved' : 'unsolved';
}
function isSolved(current) {
return isSolvedString(current) === 'solved' ? true : false;
}
console.log(array1.every(isSolved)); // false
console.log(array2.every(isSolved)); // true

Will removing items from array while looping through it breake the loop, and what can I do about it?

I'm looping through an array like this.
onScreenData.getTasks().array.forEach(function (task, taski, array) {
if(task.checked){
myTasks.addTask(task, state.name)
onScreenData.removeTask(taski);
}
})
onScreenData.removeTask() contains tasks.array.splice(index, 1);. Tasks.array is the same array as the one getting looped through. When tasks.array.splice(index, 1); is run, the loop stops instantly. What can be done about this?
As per the ECMA Script 5.1 standard Specification for Array.prototype.forEach,
The range of elements processed by forEach is set before the first call to callbackfn. Elements which are appended to the array after the call to forEach begins will not be visited by callbackfn. If existing elements of the array are changed, their value as passed to callback will be the value at the time forEach visits them; elements that are deleted after the call to forEach begins and before being visited are not visited.
To understand this better, please check the following code
var a = [1, 2, 3, 4];
a.forEach(function(currentItem, idx, array) {
console.log(a.length, currentItem, idx, array);
a.splice(idx, 1);
});
console.log(a);
It outputs,
4 1 0 [ 1, 2, 3, 4 ]
3 3 1 [ 2, 3, 4 ]
[ 2, 4 ]
In the first iteration, a.length is 4, current item is 1 and the index is 0. This is perfectly fine. And we remove the element at index 0 in the first iteration.
In the second iteration, since the first element is removed, length of the array shrinks to 3, 2 becomes the first element and the current index is 1. Now, the item corresponding to index 1 is 3. So, that is removed in this iteration.
Now, the length of the array is 2 and the current index becomes 2. Since, we cannot access the element at index 2, when the size of the array itself is 2, JavaScript breaks out of the loop.
So, effectively we have skipped the elements next to the elements being removed.
That is why we should not alter the array while iterating it.
The best option here is to use, Array.prototype.filter, like this
onScreenData.array = onScreenData.getTasks().array.filter(function (task) {
if(task.checked) {
myTasks.addTask(task, state.name)
return false;
}
return true;
});

Categories