iterable-iterator/round-robin

View on GitHub
src/roundRobin.js

Summary

Maintainability
A
25 mins
Test Coverage
import {len} from '@functional-abstraction/operator';
import {cycle} from '@iterable-iterator/cycle';
import {iter} from '@iterable-iterator/iter';
import {map} from '@iterable-iterator/map';
import {slice} from '@iterable-iterator/slice';

/**
 * Yields the first item of the first input iterable, then the first item of
 * the second input iterable, etc., until the last input iterable. Then start
 * again with the second item of the first input iterable, etc. If one of the
 * input iterable is exhausted, it is removed from the list of input iterables
 * and the algorithm continues until all input iterables have been exhausted.
 *
 * @example
 * // returns ['A','D','E','B','F','C]
 * list( roundrobin(['ABC', 'D', 'EF']) )
 *
 * @param {Iterable[]} iterables - The input iterables.
 * @returns {IterableIterator}
 *
 */
export default function* roundRobin(iterables) {
    let pending = len(iterables);

    let iterators = cycle(map(iter, iterables));

    while (pending) {
        while (true) {
            const iterator = iterators.next().value;

            const it = iterator.next();

            if (it.done) {
                break;
            }

            yield it.value;
        }

        --pending;

        iterators = cycle(slice(iterators, 0, pending, 1));
    }
}