Node.js: Combining forEach() with early returns
My Problem
The ESLint airbnb policy disallows for...of
loop iterations, and prefers forEach((element) => { code });
. However, inner returns from that loop are swallowed - they are considered returns
of the anonymous function rather than the function that embraces the loop.
Code
Original
Works, but breaks eslint-config-airbnb.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator1() {
const wordlist = ;
for (const word of words) {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator1());
Alternative 1: Iterating array indices
Works, but the style is outdated and I have to manually assign values by indices.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator2() {
const wordlist = ;
for (let i = 0; i < words.length; i += 1) {
const word = words[i];
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator2());
Alternative 2: Using forEach
Adheres to the style guide, but does not work - the inner returns are considered returns from the anonymous function, rather than the NestedIterator3
.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.forEach((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
});
return wordlist;
}
console.log(NestedIterator3());
My Question
How can a function iterate over an array while allowing early returns and avoiding indices and for..of
iterations?
node.js short-circuiting early-return
add a comment |
My Problem
The ESLint airbnb policy disallows for...of
loop iterations, and prefers forEach((element) => { code });
. However, inner returns from that loop are swallowed - they are considered returns
of the anonymous function rather than the function that embraces the loop.
Code
Original
Works, but breaks eslint-config-airbnb.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator1() {
const wordlist = ;
for (const word of words) {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator1());
Alternative 1: Iterating array indices
Works, but the style is outdated and I have to manually assign values by indices.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator2() {
const wordlist = ;
for (let i = 0; i < words.length; i += 1) {
const word = words[i];
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator2());
Alternative 2: Using forEach
Adheres to the style guide, but does not work - the inner returns are considered returns from the anonymous function, rather than the NestedIterator3
.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.forEach((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
});
return wordlist;
}
console.log(NestedIterator3());
My Question
How can a function iterate over an array while allowing early returns and avoiding indices and for..of
iterations?
node.js short-circuiting early-return
3
Well is it really necessary? I defer to the actual reason in the style guide: "Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.". Whilst I agree that avoiding mutability is grand, saying thatfor..in
orfor..of
is prone to breaking that is a bit of a stretch. Therefore the rule seems questionable and probably should be overridden
– Neil Lunn
Nov 25 '18 at 10:34
throwing exception inside offorEach
callback withtry .. catch
would work likebreak
but I believe it'd be more confusing than eslint warning or per-file suppressing the rule
– skyboyer
Nov 25 '18 at 10:39
1
@skyboyer Agree. It's not exactly a "unique" question and has certainly been asked before Short circuit Array.forEach like calling break. I'd rather the OP generally acknowledge the duplicate for themselves. Certainly can't see how there would be a completely different answer from what is already there.
– Neil Lunn
Nov 25 '18 at 10:56
add a comment |
My Problem
The ESLint airbnb policy disallows for...of
loop iterations, and prefers forEach((element) => { code });
. However, inner returns from that loop are swallowed - they are considered returns
of the anonymous function rather than the function that embraces the loop.
Code
Original
Works, but breaks eslint-config-airbnb.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator1() {
const wordlist = ;
for (const word of words) {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator1());
Alternative 1: Iterating array indices
Works, but the style is outdated and I have to manually assign values by indices.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator2() {
const wordlist = ;
for (let i = 0; i < words.length; i += 1) {
const word = words[i];
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator2());
Alternative 2: Using forEach
Adheres to the style guide, but does not work - the inner returns are considered returns from the anonymous function, rather than the NestedIterator3
.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.forEach((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
});
return wordlist;
}
console.log(NestedIterator3());
My Question
How can a function iterate over an array while allowing early returns and avoiding indices and for..of
iterations?
node.js short-circuiting early-return
My Problem
The ESLint airbnb policy disallows for...of
loop iterations, and prefers forEach((element) => { code });
. However, inner returns from that loop are swallowed - they are considered returns
of the anonymous function rather than the function that embraces the loop.
Code
Original
Works, but breaks eslint-config-airbnb.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator1() {
const wordlist = ;
for (const word of words) {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator1());
Alternative 1: Iterating array indices
Works, but the style is outdated and I have to manually assign values by indices.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator2() {
const wordlist = ;
for (let i = 0; i < words.length; i += 1) {
const word = words[i];
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator2());
Alternative 2: Using forEach
Adheres to the style guide, but does not work - the inner returns are considered returns from the anonymous function, rather than the NestedIterator3
.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.forEach((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
});
return wordlist;
}
console.log(NestedIterator3());
My Question
How can a function iterate over an array while allowing early returns and avoiding indices and for..of
iterations?
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator1() {
const wordlist = ;
for (const word of words) {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator1());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator1() {
const wordlist = ;
for (const word of words) {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator1());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator2() {
const wordlist = ;
for (let i = 0; i < words.length; i += 1) {
const word = words[i];
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator2());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator2() {
const wordlist = ;
for (let i = 0; i < words.length; i += 1) {
const word = words[i];
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
}
return wordlist;
}
console.log(NestedIterator2());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.forEach((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
});
return wordlist;
}
console.log(NestedIterator3());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.forEach((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
if (wordlist.length >= MAX_WORDS) {
return wordlist;
}
});
return wordlist;
}
console.log(NestedIterator3());
node.js short-circuiting early-return
node.js short-circuiting early-return
asked Nov 25 '18 at 10:22
Adam MatanAdam Matan
50.7k95266421
50.7k95266421
3
Well is it really necessary? I defer to the actual reason in the style guide: "Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.". Whilst I agree that avoiding mutability is grand, saying thatfor..in
orfor..of
is prone to breaking that is a bit of a stretch. Therefore the rule seems questionable and probably should be overridden
– Neil Lunn
Nov 25 '18 at 10:34
throwing exception inside offorEach
callback withtry .. catch
would work likebreak
but I believe it'd be more confusing than eslint warning or per-file suppressing the rule
– skyboyer
Nov 25 '18 at 10:39
1
@skyboyer Agree. It's not exactly a "unique" question and has certainly been asked before Short circuit Array.forEach like calling break. I'd rather the OP generally acknowledge the duplicate for themselves. Certainly can't see how there would be a completely different answer from what is already there.
– Neil Lunn
Nov 25 '18 at 10:56
add a comment |
3
Well is it really necessary? I defer to the actual reason in the style guide: "Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.". Whilst I agree that avoiding mutability is grand, saying thatfor..in
orfor..of
is prone to breaking that is a bit of a stretch. Therefore the rule seems questionable and probably should be overridden
– Neil Lunn
Nov 25 '18 at 10:34
throwing exception inside offorEach
callback withtry .. catch
would work likebreak
but I believe it'd be more confusing than eslint warning or per-file suppressing the rule
– skyboyer
Nov 25 '18 at 10:39
1
@skyboyer Agree. It's not exactly a "unique" question and has certainly been asked before Short circuit Array.forEach like calling break. I'd rather the OP generally acknowledge the duplicate for themselves. Certainly can't see how there would be a completely different answer from what is already there.
– Neil Lunn
Nov 25 '18 at 10:56
3
3
Well is it really necessary? I defer to the actual reason in the style guide: "Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.". Whilst I agree that avoiding mutability is grand, saying that
for..in
or for..of
is prone to breaking that is a bit of a stretch. Therefore the rule seems questionable and probably should be overridden– Neil Lunn
Nov 25 '18 at 10:34
Well is it really necessary? I defer to the actual reason in the style guide: "Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.". Whilst I agree that avoiding mutability is grand, saying that
for..in
or for..of
is prone to breaking that is a bit of a stretch. Therefore the rule seems questionable and probably should be overridden– Neil Lunn
Nov 25 '18 at 10:34
throwing exception inside of
forEach
callback with try .. catch
would work like break
but I believe it'd be more confusing than eslint warning or per-file suppressing the rule– skyboyer
Nov 25 '18 at 10:39
throwing exception inside of
forEach
callback with try .. catch
would work like break
but I believe it'd be more confusing than eslint warning or per-file suppressing the rule– skyboyer
Nov 25 '18 at 10:39
1
1
@skyboyer Agree. It's not exactly a "unique" question and has certainly been asked before Short circuit Array.forEach like calling break. I'd rather the OP generally acknowledge the duplicate for themselves. Certainly can't see how there would be a completely different answer from what is already there.
– Neil Lunn
Nov 25 '18 at 10:56
@skyboyer Agree. It's not exactly a "unique" question and has certainly been asked before Short circuit Array.forEach like calling break. I'd rather the OP generally acknowledge the duplicate for themselves. Certainly can't see how there would be a completely different answer from what is already there.
– Neil Lunn
Nov 25 '18 at 10:56
add a comment |
1 Answer
1
active
oldest
votes
One option would be to use reduce
, which is very flexible and can be used in many situations where the other iteration methods aren't sufficient - only push
to the accumulator if the accumulator's length is smaller than MAX_WORDS
and of the word's length is sufficient:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
Still, the above method does iterate over all indicies - it doesn't actually return early, it just doesn't do anything in the later iterations once the end condition has been fulfilled. If you want to actually break out of the iterator, you could use .some
instead, although it's even more impure, and the intent of the code is slightly less clear IMO:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
For this particular example, you could also use filter
followed by slice
:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
which certainly looks far more elegant, but the .filter
necessarily iterates over all items in the array first, and so has the same problem as the reduce
(no short-circuiting is going on) - in addition, those two chained methods only represent a subset of the situations in which one might want to short-circuit array iteration.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53466565%2fnode-js-combining-foreach-with-early-returns%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
One option would be to use reduce
, which is very flexible and can be used in many situations where the other iteration methods aren't sufficient - only push
to the accumulator if the accumulator's length is smaller than MAX_WORDS
and of the word's length is sufficient:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
Still, the above method does iterate over all indicies - it doesn't actually return early, it just doesn't do anything in the later iterations once the end condition has been fulfilled. If you want to actually break out of the iterator, you could use .some
instead, although it's even more impure, and the intent of the code is slightly less clear IMO:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
For this particular example, you could also use filter
followed by slice
:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
which certainly looks far more elegant, but the .filter
necessarily iterates over all items in the array first, and so has the same problem as the reduce
(no short-circuiting is going on) - in addition, those two chained methods only represent a subset of the situations in which one might want to short-circuit array iteration.
add a comment |
One option would be to use reduce
, which is very flexible and can be used in many situations where the other iteration methods aren't sufficient - only push
to the accumulator if the accumulator's length is smaller than MAX_WORDS
and of the word's length is sufficient:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
Still, the above method does iterate over all indicies - it doesn't actually return early, it just doesn't do anything in the later iterations once the end condition has been fulfilled. If you want to actually break out of the iterator, you could use .some
instead, although it's even more impure, and the intent of the code is slightly less clear IMO:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
For this particular example, you could also use filter
followed by slice
:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
which certainly looks far more elegant, but the .filter
necessarily iterates over all items in the array first, and so has the same problem as the reduce
(no short-circuiting is going on) - in addition, those two chained methods only represent a subset of the situations in which one might want to short-circuit array iteration.
add a comment |
One option would be to use reduce
, which is very flexible and can be used in many situations where the other iteration methods aren't sufficient - only push
to the accumulator if the accumulator's length is smaller than MAX_WORDS
and of the word's length is sufficient:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
Still, the above method does iterate over all indicies - it doesn't actually return early, it just doesn't do anything in the later iterations once the end condition has been fulfilled. If you want to actually break out of the iterator, you could use .some
instead, although it's even more impure, and the intent of the code is slightly less clear IMO:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
For this particular example, you could also use filter
followed by slice
:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
which certainly looks far more elegant, but the .filter
necessarily iterates over all items in the array first, and so has the same problem as the reduce
(no short-circuiting is going on) - in addition, those two chained methods only represent a subset of the situations in which one might want to short-circuit array iteration.
One option would be to use reduce
, which is very flexible and can be used in many situations where the other iteration methods aren't sufficient - only push
to the accumulator if the accumulator's length is smaller than MAX_WORDS
and of the word's length is sufficient:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
Still, the above method does iterate over all indicies - it doesn't actually return early, it just doesn't do anything in the later iterations once the end condition has been fulfilled. If you want to actually break out of the iterator, you could use .some
instead, although it's even more impure, and the intent of the code is slightly less clear IMO:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
For this particular example, you could also use filter
followed by slice
:
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
which certainly looks far more elegant, but the .filter
necessarily iterates over all items in the array first, and so has the same problem as the reduce
(no short-circuiting is going on) - in addition, those two chained methods only represent a subset of the situations in which one might want to short-circuit array iteration.
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words.reduce((wordlist, word) => {
if (wordlist.length < MAX_WORDS && word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist;
}, );
}
console.log(NestedIterator3());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
const wordlist = ;
words.some((word) => {
if (word.length >= MIN_WORD_SIZE) {
wordlist.push(word);
}
return wordlist.length === MAX_WORDS;
}, );
return wordlist;
}
console.log(NestedIterator3());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
const words = ['harry', 'potter', 'and', 'the', 'forbidden', 'journey'];
const MIN_WORD_SIZE = 4;
const MAX_WORDS = 3;
function NestedIterator3() {
return words
.filter(word => word.length >= MIN_WORD_SIZE)
.slice(0, MAX_WORDS)
}
console.log(NestedIterator3());
edited Nov 25 '18 at 11:13
answered Nov 25 '18 at 10:34
CertainPerformanceCertainPerformance
92.4k165383
92.4k165383
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53466565%2fnode-js-combining-foreach-with-early-returns%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
3
Well is it really necessary? I defer to the actual reason in the style guide: "Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects.". Whilst I agree that avoiding mutability is grand, saying that
for..in
orfor..of
is prone to breaking that is a bit of a stretch. Therefore the rule seems questionable and probably should be overridden– Neil Lunn
Nov 25 '18 at 10:34
throwing exception inside of
forEach
callback withtry .. catch
would work likebreak
but I believe it'd be more confusing than eslint warning or per-file suppressing the rule– skyboyer
Nov 25 '18 at 10:39
1
@skyboyer Agree. It's not exactly a "unique" question and has certainly been asked before Short circuit Array.forEach like calling break. I'd rather the OP generally acknowledge the duplicate for themselves. Certainly can't see how there would be a completely different answer from what is already there.
– Neil Lunn
Nov 25 '18 at 10:56