Path: blob/trunk/third_party/closure/goog/collections/iters.js
4212 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Utilities for working with ES6 iterables.8*9* The goal is that this should be a replacement for goog.iter which uses10* a now non-standard approach to iterables.11*12* This module's API should track the TC39 proposal as closely as possible to13* allow for eventual deprecation and migrations.14* https://github.com/tc39/proposal-iterator-helpers15*16* @see go/closure-iters-labs17* @see https://goo.gl/Rok5YQ18*/1920goog.module('goog.collections.iters');21goog.module.declareLegacyNamespace();2223/**24* Get the iterator for an iterable.25* @param {!Iterable<VALUE>} iterable26* @return {!Iterator<VALUE>}27* @template VALUE28*/29function getIterator(iterable) {30return iterable[goog.global.Symbol.iterator]();31}32exports.getIterator = getIterator;333435/**36* Call a function with every value of an iterable.37*38* Warning: this function will never halt if given an iterable that39* is never exhausted.40*41* @param {!Iterator<VALUE>} iterator42* @param {function(VALUE) : *} f43* @template VALUE44*/45function forEach(iterator, f) {46let result;47while (!(result = iterator.next()).done) {48f(result.value);49}50}51exports.forEach = forEach;5253/**54* An Iterable that wraps a child iterable, and maps every element of the child55* iterator to a new value, using a mapping function. Similar to Array.map, but56* for Iterable.57* @template TO,FROM58* @implements {IteratorIterable<TO>}59*/60class MapIterator {61/**62* @param {!Iterable<FROM>} childIter63* @param {function(FROM): TO} mapFn64*/65constructor(childIter, mapFn) {66/** @private @const {!Iterator<FROM>} */67this.childIterator_ = getIterator(childIter);6869/** @private @const {function(FROM): TO} */70this.mapFn_ = mapFn;71}7273[Symbol.iterator]() {74return this;75}7677/** @override */78next() {79const childResult = this.childIterator_.next();80// Always return a new object, even when childResult.done == true. This is81// so that we don't accidentally preserve generator return values, which82// are unlikely to be meaningful in the context of this MapIterator.83return {84value: childResult.done ? undefined :85this.mapFn_.call(undefined, childResult.value),86done: childResult.done,87};88}89}909192/**93* Maps the values of one iterable to create another iterable.94*95* When next() is called on the returned iterable, it will call the given96* function `f` with the next value of the given iterable97* `iterable` until the given iterable is exhausted.98*99* @param {!Iterable<VALUE>} iterable100* @param {function(VALUE): RESULT} f101* @return {!IteratorIterable<RESULT>} The created iterable that gives the102* mapped values.103* @template VALUE, RESULT104*/105exports.map = function(iterable, f) {106return new MapIterator(iterable, f);107};108109110/**111* An Iterable that wraps a child Iterable and returns a subset of the child's112* items, based on a filter function. Similar to Array.filter, but for113* Iterable.114* @template T115* @implements {IteratorIterable<T>}116*/117class FilterIterator {118/**119* @param {!Iterable<T>} childIter120* @param {function(T): boolean} filterFn121*/122constructor(childIter, filterFn) {123/** @private @const {!Iterator<T>} */124this.childIter_ = getIterator(childIter);125126/** @private @const {function(T): boolean} */127this.filterFn_ = filterFn;128}129130[Symbol.iterator]() {131return this;132}133134/** @override */135next() {136while (true) {137const childResult = this.childIter_.next();138if (childResult.done) {139// Don't return childResult directly, because that would preserve140// generator return values, and we want to ignore them.141return {done: true, value: undefined};142}143const passesFilter = this.filterFn_.call(undefined, childResult.value);144if (passesFilter) {145return childResult;146}147}148}149}150151152/**153* Filter elements from one iterator to create another iterable.154*155* When next() is called on the returned iterator, it will call next() on the156* given iterator and call the given function `f` with that value until `true`157* is returned or the given iterator is exhausted.158*159* @param {!Iterable<VALUE>} iterable160* @param {function(VALUE): boolean} f161* @return {!IteratorIterable<VALUE>} The created iterable that gives the mapped162* values.163* @template VALUE164*/165exports.filter = function(iterable, f) {166return new FilterIterator(iterable, f);167};168169170/**171* @template T172* @implements {IteratorIterable<T>}173*/174class ConcatIterator {175/** @param {!Array<!Iterator<T>>} iterators */176constructor(iterators) {177/** @private @const {!Array<!Iterator<T>>} */178this.iterators_ = iterators;179180/** @private {number} */181this.iterIndex_ = 0;182}183184[Symbol.iterator]() {185return this;186}187188/** @override */189next() {190while (this.iterIndex_ < this.iterators_.length) {191const result = this.iterators_[this.iterIndex_].next();192if (!result.done) {193return result;194}195this.iterIndex_++;196}197return /** @type {!IIterableResult<T>} */ ({done: true});198}199}200201202/**203* Concatenates multiple iterators to create a new iterable.204*205* When next() is called on the return iterator, it will call next() on the206* current passed iterator. When the current passed iterator is exhausted, it207* will move on to the next iterator until there are no more left.208*209* All generator return values will be ignored (i.e. when childIter.next()210* returns {done: true, value: notUndefined} it will be treated as just211* {done: true}).212*213* @param {...!Iterable<VALUE>} iterables214* @return {!IteratorIterable<VALUE>}215* @template VALUE216*/217exports.concat = function(...iterables) {218return new ConcatIterator(iterables.map(getIterator));219};220221/**222* Creates an array containing the values from the given iterator.223* @param {!Iterator<VALUE>} iterator224* @return {!Array<VALUE>}225* @template VALUE226*/227exports.toArray = function(iterator) {228const arr = [];229forEach(iterator, e => arr.push(e));230return arr;231};232233234