Iterables and iterators

  • 1. Iterable refers to objects that can be looped through for/of, which is a feature of ES6, including (arrays, strings, sets, maps).

  • 2. Extension operators… You can expand an iterable

let chars = [..."abcd"] // chars === ["a","b","c","d"]
let data = [1.2.3.4]
Math.max(data) / / 4
Copy the code
  • Iterators can be used to deconstruct assignments

let purple = Uint8Array.of(255.0.255.128)
let [r, g, b, a] = purple // a===128
Copy the code
  • 4. Iterate over the map object and return the [key,value] pair

let m = new Map([["one".1], ["two".2]])
for (let [k, v] of m) console.log(k, v);
Copy the code
  • 5. Values Iterate over keys or values, using keys() or values()

[...m]; // [["one", 1], ["two", 2]]
[...m.entries()]; // [["one", 1], ["two", 2]]
[...m.keys()]; // ["one","two"]
[...m.values()]; / / [1, 2]
Copy the code
  • 6. Can accept built-in functions and constructors of Array objects, can accept any iterator

new Set("abc"); new Set(["a"."b"."c"]); // They are the same
Copy the code
  • 7. Iterator principle

Iterable: an object that has an iterator and that method returns an iterator
let list1 = [1.2]
// Iterator object: a list of objects with a next() method that returns the result of the iteration
let iterator = list1[Symbol.iterator]() // There is a symbol. iterator method that returns itself
// Result of iteration: objects with attributes value and done
for (letresult = iterator.next(); ! result.done; result = iterator.next()) {console.log(result.value);
}
/ / case
let list2 = [1.2.3.4.5]
let iter = list2[Symbol.iterator]()
console.log(iter.next().value); / / 1
console.log([...iter]); // [2, 3, 4, 5]
Copy the code

Implementation of iterable

Rule: As long as a data type represents some iterable structure, it should be implemented as an iterable

Implementation: In order for a class to be iterable, we must implement a symbol.iterator method that must return an iterator object with a next() method, and the next() method must return an object with value and done attributes

Actions: Iterable numeric Range classes

  • {x: form<=x<=to}

/* Range defines the has() method, which tests if a given value is a member of the Range that is iterable, iterating over all integers within its Range */
class Range {
    constructor(from, to) {
        this.from = from
        this.to = to
    }
    has(x) {
        return typeof x === 'number' &&
            x >= this.from &&
            x <= this.to
    }
    toString() { return `{x: The ${this.from}<=x <=The ${this.to}} ` }
    [Symbol.iterator]() {
        let next = Math.ceil(this.from);
        let last = this.to
        return {
            next() {
                return {
                    done: next > last,
                    value: next++,
                }
            }
        }
    }
}


console.log(new Range(1.3).toString());
console.log(new Range(1.3).has(4));

for (const i of new Range(1.3)) {
    console.log(i);
}

console.log([...new Range(-1.3)]);
Copy the code
  • 2. Define functions that return iterable values

// Replace the map method for arrays
function map(iterable, fn) {
    let iterator = iterable[Symbol.iterator]()
    // Returns an iterable, also an iterator
    return{[Symbol.iterator]() { return this },
        next() {
            let obj = iterator.next()
            if (obj.done) {
                return obj
            } else {
                return { value: fn(obj.value) }
            }
        }
    }
}
console.log([...map([1.2.3].x= > x * x)]);
// Replace the array filter method
function filter(iterable, fn) {
    let iterator = iterable[Symbol.iterator]()
    // Returns an iterable, also an iterator
    return{[Symbol.iterator]() { return this },
        next() {
            while (true) {
                let obj = iterator.next()
                if (fn(obj.value) || obj.done) {
                    return obj
                }
            }
        }
    }
}
console.log([...filter([1.2.3.4.5.6.12.56].x= > x > 3)]);
Copy the code
  • 3. Lazy iteration

// Suppose a very long string wants to be separated by Spaces. If you use the string split method, then to process the entire string,
// It takes up a lot of memory to store the returned array and its strings.
// With lazy iterations, you don't have to keep them all in memory
function words(s) {
    let r = /\s+|$/g
    r.lastIndex = s.match(/ [^] /).index
    return{[Symbol.iterator]() { return this },
        next() {
            let start = r.lastIndex
            if (start < s.length) { let match = r.exec(s) if (match) { return { value: s.substring(start, match.index) } } }
            return { done: true}}}}console.log([...words(" abc def ghi ")]);
Copy the code

Concepts and examples of generators

Rule: Generators are iterators defined by es6 syntax, suitable for scenarios where the value to be iterated is not an element of a data structure, but rather the result of a calculation

Implementation: To create a generator, define a generator function using the keyword function*. Calling a generator function does not execute the function body, but returns an iterator.

Action: Calling the iterator’s next method causes the generator function body to execute from scratch until a yield statement is encountered. The value of the yield statement becomes the return value. Yield and yield* can only be used in generator functions; regular functions cannot occur!

  • Example 1.

// 例1
function* oneDigitPrimes() {
    yield 2;
    yield 3;
    yield 5;
    yield 7;
}
let primes = oneDigitPrimes()
primes.next().value / / 2
primes.next().value / / 3
console.log(primes.next().value); / / 5
console.log(primes.next().done); // false
console.log(primes.next().done); // true

let primesIterator = primes[Symbol.iterator]()// Iterable
console.log(primesIterator.next()); // {value: undefined, done: true}

console.log([...oneDigitPrimes()]); / / [2, 3, 5, 7)
let sum = 0
for (const v of oneDigitPrimes()) {
    sum += v
}
console.log(sum); / / 17

// 例2
const seq = function* (from, to) {
    for (let i = from; i <= to; i++) yield i
}
console.log([...seq(3.5)]); / / [3, 4, 5)

Example 3 class and object literals. You cannot use arrow functions to define generator functions
let o = {
    x: 1.y: 2.z: 3,
    *g() {
        for (const key of Object.keys(this)) {
            yield key
        }
    }
}
console.log([...o.g()]); //["x", "y", "z", "g"]

// Example 4 Infinite Fibonacci loops until memory runs out if [...fibonacciSequence()] is executed
function* fibonacciSequence() {
    let x = 0, y = 1;
    while (1) {
        yield y;
        [x, y] = [y, x + y]
    }
}

// Return the 20th Fibonacci number with the constraint
function fibonacci(n) {
    for (const f of fibonacciSequence()) {
        if (n-- <= 0) {
            return f
        }
    }
}
console.log(fibonacci(10)); / / 89

// Echoes the first n elements of the specified iterable in conjunction with the take generator (encapsulated by another generator)
function* take(n, iterable) {
    let it = iterable[Symbol.iterator]()
    while (n-- > 0) {
        let next = it.next()
        if (next.done) {
            return
        }
        yield next.value
    }
}
console.log([...take(5, fibonacciSequence())]); //[1, 1, 2, 3, 5]

// Get the array of iterables, echo back the elements alternately
function* zip(. iterables) {
    // Get an iterator for each iterable
    let iterators = iterables.map(i= > i[Symbol.iterator]())
    let index = 0
    while (iterators.length > 0) {
        if (index >= iterators.length) {
            index = 0
        }
        let item = iterators[index].next()
        if (item.done) {
            iterators.splice(index, 1)}else {
            yield item.value
            index++
        }
    }
}
console.log([...zip(oneDigitPrimes(), 'ab'[0]]);//[2, "a", 0, 3, "b", 5, 7]

// yield* to a recursive generator
// Sequentially loopback elements
function* sequence(. iterables) {
    for (const可迭代of iterables) {
        for (const item of iterable) {
            yielditem; }}}console.log([...sequence("abc"[1.2.3]]);// ["a", "b", "c", 1, 2, 3]
// Use yield* to simplify
function* sequence2(. iterables) {
    for (const item of iterables) {
        yield* item
    }
}
console.log([...sequence2('abc'.'def')]); // ["a", "b", "c", "d", "e", "f"]    
Copy the code
  • 2. Return value of generator function

function* oneAndDone() {
    yield 1
    return "done"
}
// Normal iteration does not return a value
console.log([...oneAndDone()]); / / [1]
// This can be obtained by calling next explicitly
let generator = oneAndDone()
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 'done', done: true }
console.log(generator.next()); // { value: undefined, done: true }
Copy the code