Sequence

finite/infinite iterable of numbers

JSiterationiterablecustom ⟩ Sequence

💾replit:(iterable) Sequence

// ⭐️ Sequence (iterable class)
//
// use cases:
//     new Sequence()    // 0, 1, 2, 3 ... (infinite)
// -------------------------------------------------------
class Sequence {

    // 🔸 custom class type name
    get [Symbol.toStringTag]() { return 'Sequence' }
    
    // init
    constructor({
        start = 0,
        step  = 1,             // step between numbers
        end   = undefined,     // end of sequence
        terms = undefined,     // how many terms the sequence has
    }={}) {
        
        this.start = start;
        this.step  = step;
        this.end   = end;      // ⭐️ ignored if `this.terms` is explicitly defined.
        this.terms = terms;    // ⭐️ `this.terms` has higher priority than `this.end`
        
        // finite or infnite sequence
        this.isInfinite = (this.end === undefined) && (this.terms === undefined);
        this.isFinite = !this.isInfinite;

        // ⭐️ if finite, calculate the correct number of terms,
        //    hence, `this.terms` is always defined in this case.
        if (this.isFinite) {
            // - if `this.terms` is defined, `this.end` is ignored.
            //   (the only use of `end` is to calculate the nubmer of terms, if it's not already defined)
            // - if `this.terms` is not defined, `this.end` MUST be defined.
            //   (we calculate the number of terms from `start, end, step`.)
            if (this.terms === undefined) {
                this.terms = Math.max(0, Math.floor((this.end - this.start) / this.step + 1));
            }
        }
    }
    
    // 🔸 "make-iterator" method
    [Symbol.iterator]() {
    
        let counter = 0;
        let currentValue = this.start;
    
        // ⭐️ return the iterator
        return  {                    // new "iterator" object

            // -------------------------------------------------
            //     ⭐️⭐️⭐️ arrow function as method ⭐️⭐️⭐️
            // -------------------------------------------------
            //
            // ⭐️ in the body of following next(), return() methods:
            // ❗ `this` does NOT refer to the new "iterator" object itself❗
            // ❗ `this` === the `Sequence` object in the outer context❗
            
            // 🔸 next iteration result (❗arrow function as method❗)
            next: () => {
                
                // if finite, check `this.terms` (because it's always properly defined)
                // otherwise, always return the iteration result. 
                if (this.isFinite ? counter < this.terms : true) {
                    const result = { value: currentValue };
                    currentValue += this.step;
                    counter += 1;
                    return result;    // return iteration result
                }
                
                // the iteration is done
                return { done: true };
            },
            
            // 🔸 clean up when iteration stops prematurely
            return: () => {
                console.log(`${this}: cleaning up...`);
                return { done: true }; // ⭐️ can't omit this result object although it's ignored.
            }
            
        } // end: return iterator
    
    } // end: "make-iterator" method

} // end: class Sequence

Last updated