Search code examples
javascriptloopsiterator

Creating iterable-based alternative to the filter() method of JavaScript arrays


I am a beginner reading the chapter Iterators from the book

JavaScript: The Definitive Guide

I am not able to understand the next() method in the below example from book which is about Creating iterable-based alternative to the filter() method of JavaScript arrays.

// Return an iterable object that filters the specified iterable,
// iterating only those elements for which the predicate returns true
function filter(iterable, predicate) {
    let iterator = iterable[Symbol.iterator]();
    return { // This object is both iterator and iterable
        [Symbol.iterator]() {
            return this;
        },
        next() {
            for(;;) {
                let v = iterator.next();
                if(v.done || predicate(v.value)) {
                    return v;
                }
            }
        }
    };
}

Let's test this function with an array.

let a = [1,2,3,4,5]
let iter = filter(a, x => x>3) // Call the function

// Lets check the returned object of iter.next() method
iter.next() // {value: 4, done: false}
iter.next() // {value: 5, done: false}
iter.next() // {value: undefined, done: true}

I am completely confused about the next() method here. Please help me in understanding it.

Also, I am not able to understand that... the next() method contains an infinite for loop. But what it makes it break that loop. There is no condition like that here.

Thanks in Advance


Solution

  • Also, I am not able to understand that... the next() method contains an infinite for loop. But what it makes it break that loop. There is no condition like that here.

    return breaks the loop.

    I am completely confused about the next() method here. Please help me in understanding it.

    Here's what it's doing:

    1. It calls next to get the next result object, which it stores in v.
    2. If v.done is truthy, it returns v (breaking the loop). This means the iterator has reached the end of its sequence.
    3. If v.done is falsy, it calls predicate passing in v.value; if predicate returns a truthy value, next returns v (breaking the loop).

    So the result is that the iterator returned by that filter function will iterate the values that the predicate returns a truthy value for, and will return a result object with done set to a truthy value when it reaches the end of the input interable.


    I haven't read the recent editions of the book (I bought mine something like 15 years ago), but my guess is that the given code is meant to show how you would use an iterator explicitly, and later Flanagan will introduce for-of and generator functions, which provide a simpler means of doing that filter:

    function* filter(iterable, predicate) {
        for (const element of iterable) {
            if (predicate(element)) {
                yield element;
            }
        }
    }
    

    Live Example:

    function* filter(iterable, predicate) {
        for (const element of iterable) {
            if (predicate(element)) {
                yield element;
            }
        }
    }
    
    let a = [1,2,3,4,5];
    let iter = filter(a, x => x>3); // Call the function
    
    // Lets check the returned object of iter.next() method
    console.log(iter.next()); // {value: 4, done: false}
    console.log(iter.next()); // {value: 5, done: false}
    console.log(iter.next()); // {value: undefined, done: true}

    I went back and forth between implicit (for-of, generators) and explicit in Chapter 6 of my recent book JavaScript: The New Toys. I found interleaving them was the best I could do to help get the concepts but also the details across.