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?
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 - onlypushto the accumulator if the accumulator's length is smaller thanMAX_WORDSand of the word's length is sufficient: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
.someinstead, although it's even more impure, and the intent of the code is slightly less clear IMO:For this particular example, you could also use
filterfollowed byslice:which certainly looks far more elegant, but the
.filternecessarily iterates over all items in the array first, and so has the same problem as thereduce(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.