Continue inside a forEach loop - javascript

It is standard practice to continue inside a loop if a certain condition is met/unmet. In a Javascript forEach loop, this produces a syntax error:
const values = [1, 2, 3, 4, 5];
values.forEach((value) => {
if (value === 3) { continue; }
console.log(value);
})
SyntaxError[ ... ]: Illegal continue statement: no surrounding iteration statement
This happens whether I use function or an arrow function. How can you continue inside a forEach loop?
Obviously, you could do an inverse case (if (value !== 3) { ... }), but that is not what I'm looking for.

As #robertklep stated forEach() is not a loop, it is a function. You cannot use the continue keyword inside a forEach loop because its functionality is meant to loop each item in the array.
To achieve the similar behavior you can use,
for(let item of values){
if (item === 3) {
continue;
}
console.log(item);
}

For practical purposes, return in a forEach() callback is equivalent to continue in a conventional for loop but it isn't the most idiomatic use of functional programming patterns
To solve that, you can filter before you loop:
const values = [1, 2, 3, 4, 5];
values.filter(v => v !== 3).forEach((value) => {
console.log(value);
})

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

Why map or reduce keeps running without any condition given?

const array = [7, 2, 4, 1, 10, 6, 5, 11]
const max = array.reduce((acc, val) => {
console.log(val, acc)
return val > acc ? val : acc
}, 0)
console.log(max)
I was looking at this code of reduce array method, one thing I couldn't understand at all is, How the reducer function is going to the next iteration? There is no condition that forces the reducer function to go to the next element in the array. In the first iteration, the val is 7, the first element of the array, and acc is 0, the reducer function returns 7 as per the condition written.
My question is how the number 7 as being the new accumulator is going to be called on the reducer function. I thought the normal procedure is you have to meet some kind of condition to iterate over again and again. Is there something written in the reduce method itself? Can you explain me please?
Note that array.reduce:
reduce
calls the callback, as a function, once for each element after the
first element present in the array, in ascending order.
You could understand the reduce as a array.map but the goal of it is to change the array to a singe output.
It will loop over the whole array same with the forEach/map/...
Check below example, even though you don't do anything, like return or anything else to array.reduce, it will still work and iterate the array
You could check here for more
But of course if you don't use return for array.reduce, there will be no benefit for you to use array.reduce
const array = [7, 2, 4, 1, 10, 6, 5, 11]
const max = array.reduce((acc, val) => {
console.log(val)
}, 0)
console.log(max)
As per the docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
The reduce() method executes a user-supplied “reducer” callback function on each element of the array,
The reduce method's implementation resembles something like this:
Array.prototype.reduce = function(callback, initialValue) {
var acc = initialValue;
for(var i = 0; i < this.length; i++) {
acc = callback(acc, this[i], i);
}
return acc;
}
The reduce methods iteration condition is the array length.
The same goes for map.

Use reduce and recursion in javascript to flatten nested array

I have found this interesting code example which flattens an array using the recursion and reduce function instead of flat. I get what it does except the following:
acc = acc.concat(flatWithRec(item)); why accumulator is being reassign? how is it possible in reduce function?
and why use concat here?
return acc; why acc is being returned? is it a new Flat Array each time function is called?
is there a way to, still, use recursion and make it easier for the reader to understand?
Please clarify
function flatWithRec(arr) {
const flatArray = arr.reduce((acc, item) => {
if (Array.isArray(item)) {
acc = acc.concat(flatWithRec(item));
} else {
acc.push(item);
}
return acc;
}, []);
return flatArray;
}
console.log(flatWithRec([1, [2, 3, [4],
[5, 6, [7]]
]]))
// output: [1, 2, 3, 4, 5, 6, 7])
The accumulator is an array. You reassign it to give it the new array containing the items of the one you have at the beginning of the loop and the items of the array items to add. As said in the comments, acc.concat returns a new array containing the items of the arrays passed in parameter. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
You need to return the accumulator at the end of each loop for the new value to be taken in account at the next loop.
Javascript recursive array flattening
acc = acc.concat(flatWithRec(item)); why accumulator is being reassign? how is it possible in reduce function? and why use concat here?
The accumulator (acc) is a function argument, and can be re-assigned, although it's not a good practice to do so. Concat combines two items (array or otherwise), and returns a new array, so you need to assign it to the acc as the result of the current loop.
return acc; why acc is being returned? is it a new Flat Array each time function is called?
The accumulator holds the current state of the reduced items, in your case the current flat array after each loop. You need to return it, so the next loop can continue to accumulate.
is there a way to, still, use recursion and make it easier for the reader to understand?
My take on flatWithRec - always concat, but if it's an array call flatWithRec on it before concatenating:
function flatWithRec(arr) {
return arr.reduce((acc, item) =>
acc.concat(
Array.isArray(item)
? flatWithRec(item)
: item
), []);
}
const result = flatWithRec([1, [2, 3, [4], [5, 6, [7]]]])
console.log(result) // output: [1, 2, 3, 4, 5, 6, 7])
So, the callback for the reduce method runs for every item in the array and whatever is returned from iteration x is passed as the first argument to iteration x+1. So, it is essential to make sure that during every iteration the correct state is returned.
acc = acc.concat(flatWithRec(item)) Why accumulator is being reassigned? How is it possible in reduce function? And why use concat here?
So, we are assigning acc the return value of concat because concat does not change the original array, it returns a fresh array. Accumulator is just like any other parameter so you can reassign. Not at all necessary to use concat (see my solution at the end using push and spread).
return acc; Why acc is being returned? Is it a new flat array each time the function is called?
As, already mentioned you need to return the correct state from the callback. And yes, during each iteration a new array is being created (which is not that performant, see my solution at the end)
Is there a way to still use recursion and make it easier for the reader to understand?
Easier for reader I can't tell, but here's my solution using => functions and spread.
const flatWithRec = (arr) =>
arr.reduce((acc, item) => (
Array.isArray(item) ? acc.push(...flatWithRec(item)) : acc.push(item), acc
), []);
console.log(flatWithRec([1, [2, 3, [4], [5, 6, [7]]]]));
Yes, there is a way to use recursion and make it easier to understand:
function f(A, i=0){
return i == A.length ? [] : (Array.isArray(A[i]) ? f(A[i]) : [A[i]]).concat(f(A, i+1));
}
var A = [1, [2, 3, [4], [5, 6, [7]]]];
console.log(JSON.stringify(A));
console.log(JSON.stringify(f(A)));

Why if statements blocks in while loop?

I have an array of numbers with a bunch of duplicates. I need to get rid of them so I put the code:
let dup = arr.filter((elem, pos)=> arr.indexOf(elem) !== pos);
// dup Array contains the duplicate numbers
arr = arr.filter((elem, pos)=> arr.indexOf(elem) == pos);
//arr contains the whole array with duplicates
let i = 0;
let j = 0;
while(i<arr.length){
while(j<dup.length){
if(arr[i] == dup[j]){
arr.splice(i, 1);
//the splice method resets the decrease the index of the array so
i--;
};
j++;
};
i++
}
The problem is that the if doesn't run after the first match.So the array splice the first duplicate that it finds and stops. How can I fix that?
From Get all unique values in a JavaScript array (remove duplicates)
const myArray = ['a', 1, 'a', 2, '1'];
const unique = [...new Set(myArray)];
// output ["a", 1, 2, "1"]
or as a function
const unique = [...new Set(myArray)]
The problem is that you never reset j. You need to move that inside the while (i ...) loop.
let arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
console.log('arr', arr)
let dup = arr.filter((elem, pos)=> arr.indexOf(elem) !== pos);
console.log('dup', dup)
arr = arr.filter((elem, pos)=> arr.indexOf(elem) == pos)
console.log('new arr', arr)
let i = 0;
while(i<arr.length){
let j = 0;
while(j<dup.length){
if(arr[i] == dup[j]){
arr.splice(i, 1);
i--;
};
j++;
};
i++
}
console.log('final arr', arr)
But there are easier ways to do this.
Update
I got pinged for mentioning easier ways without showing one. Here is an alternative way to get the same results:
const singletons = (
xs,
dups = xs .filter ((x, i) => arr .indexOf (x) !== i)
) => xs .filter (x => dups .indexOf (x) < 0)
let arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
console .log (
singletons (arr)
)
This version does not modify your original array, just returning a new one containing only the singleton elements (those that appear just once in the original list.)
Explanation
singletons is a function taking an array of some type of element and returning another array of the same type. Because that type is not specific, I use a non-specific name; a fairly strong convention make this xs. with the s noting that it's plural (i.e. an array of them.)
dups is an array of elements that is duplicated in your original. Although I include it as a defaulted parameter, we could just as easily have created it in the body of the function like this:
const singletons = (xs) => {
const dups = xs .filter ((x, i) => arr .indexOf (x) !== i)
return xs .filter (x => dups .indexOf (x) < 0)
}
The only reason I didn't was that I am perhaps over-fond of single expression bodies, thus avoiding the {-} pair and the return statement. But there is no real difference between these two approaches, except that the one I presented happens to do some additional work that I would never count on: if you supply a second parameter, an array of values, then, rather than removing the duplicates, it removes all those element from your array that are also in the second one, vaguely reminiscent of a set difference function.
The main advantage of this over your approach is that it is non-destructive. It does not mutate your original data. It also has no assignments, except in the default parameter, so there is no confusion in state management. ("Where do I put let j == 0?" is not a meaningful question here.) This makes it feel more robust.
You need to set j=0 inside the first while loop, otherwise, it will only run through the second while loop once.
Also, if I were you, I would use a for-loop (array.forEach() specifically) instead of while, because they already count the number of elements anyway.
My solution for this is this:
arr.forEach((e, i) => {
dup.forEach((f, j) => {
if(e==f){
arr.splice(i, 1);
}
})
});
Hope it works for you.
EDIT:
Stolen from the comment from James to the original question.
Javascript already provides an easy method to do just that anyway:
arr.filter(x=> dup.indexOf(x) < 0)

How to use break statement in an Array method such as filter?

I was trying to solve an algorithm challenge which required;
Drop the elements of an array (first argument), starting from the
front, until the predicate (second argument) returns true.
The second argument, func, is a function you'll use to test the first
elements of the array to decide if you should drop it or not.
Return the rest of the array, otherwise return an empty array.
Though I have been able to come up with a lengthy solution to this through looping the array I was wondering if there is a way to implement the break statement inside the methods.
Could it be accomplish by redefining the Array.prototype.filter method to accept a break statement ?
Though the solution could have been easy as such the methods of arrays in JavaScript doesn't accept this. How do you bypass that?
function dropElements(arr, func) {
return arr.filter(func);
}
You can just use for loop and when function returns true you can just break loop and return results from that index.
var arr = [1, 2, 3, 4, 5, 6, 7, 8];
function drop(data, func) {
var result = [];
for (var i = 0; i < data.length; i++) {
var check = func(data[i]);
if (check) {
result = data.slice(i);
break;
}
}
return result;
}
var result = drop(arr, e => e == 4)
console.log(result)
You can also use findIndex() and if match is found you can slice array from that index otherwise return empty array.
var arr = [1, 2, 3 ,4 ,5 ,6 ,7, 8];
var index = arr.findIndex(e => e == 4)
var result = index != -1 ? arr.slice(index) : []
console.log(result)

Categories