Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/collections/iters.js
4558 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for working with ES6 iterables.
9
*
10
* The goal is that this should be a replacement for goog.iter which uses
11
* a now non-standard approach to iterables.
12
*
13
* This module's API should track the TC39 proposal as closely as possible to
14
* allow for eventual deprecation and migrations.
15
* https://github.com/tc39/proposal-iterator-helpers
16
*
17
* @see go/closure-iters-labs
18
* @see https://goo.gl/Rok5YQ
19
*/
20
21
goog.module('goog.collections.iters');
22
goog.module.declareLegacyNamespace();
23
24
/**
25
* Get the iterator for an iterable.
26
* @param {!Iterable<VALUE>} iterable
27
* @return {!Iterator<VALUE>}
28
* @template VALUE
29
*/
30
function getIterator(iterable) {
31
return iterable[goog.global.Symbol.iterator]();
32
}
33
exports.getIterator = getIterator;
34
35
36
/**
37
* Call a function with every value of an iterable.
38
*
39
* Warning: this function will never halt if given an iterable that
40
* is never exhausted.
41
*
42
* @param {!Iterator<VALUE>} iterator
43
* @param {function(VALUE) : *} f
44
* @template VALUE
45
*/
46
function forEach(iterator, f) {
47
let result;
48
while (!(result = iterator.next()).done) {
49
f(result.value);
50
}
51
}
52
exports.forEach = forEach;
53
54
/**
55
* An Iterable that wraps a child iterable, and maps every element of the child
56
* iterator to a new value, using a mapping function. Similar to Array.map, but
57
* for Iterable.
58
* @template TO,FROM
59
* @implements {IteratorIterable<TO>}
60
*/
61
class MapIterator {
62
/**
63
* @param {!Iterable<FROM>} childIter
64
* @param {function(FROM): TO} mapFn
65
*/
66
constructor(childIter, mapFn) {
67
/** @private @const {!Iterator<FROM>} */
68
this.childIterator_ = getIterator(childIter);
69
70
/** @private @const {function(FROM): TO} */
71
this.mapFn_ = mapFn;
72
}
73
74
[Symbol.iterator]() {
75
return this;
76
}
77
78
/** @override */
79
next() {
80
const childResult = this.childIterator_.next();
81
// Always return a new object, even when childResult.done == true. This is
82
// so that we don't accidentally preserve generator return values, which
83
// are unlikely to be meaningful in the context of this MapIterator.
84
return {
85
value: childResult.done ? undefined :
86
this.mapFn_.call(undefined, childResult.value),
87
done: childResult.done,
88
};
89
}
90
}
91
92
93
/**
94
* Maps the values of one iterable to create another iterable.
95
*
96
* When next() is called on the returned iterable, it will call the given
97
* function `f` with the next value of the given iterable
98
* `iterable` until the given iterable is exhausted.
99
*
100
* @param {!Iterable<VALUE>} iterable
101
* @param {function(VALUE): RESULT} f
102
* @return {!IteratorIterable<RESULT>} The created iterable that gives the
103
* mapped values.
104
* @template VALUE, RESULT
105
*/
106
exports.map = function(iterable, f) {
107
return new MapIterator(iterable, f);
108
};
109
110
111
/**
112
* An Iterable that wraps a child Iterable and returns a subset of the child's
113
* items, based on a filter function. Similar to Array.filter, but for
114
* Iterable.
115
* @template T
116
* @implements {IteratorIterable<T>}
117
*/
118
class FilterIterator {
119
/**
120
* @param {!Iterable<T>} childIter
121
* @param {function(T): boolean} filterFn
122
*/
123
constructor(childIter, filterFn) {
124
/** @private @const {!Iterator<T>} */
125
this.childIter_ = getIterator(childIter);
126
127
/** @private @const {function(T): boolean} */
128
this.filterFn_ = filterFn;
129
}
130
131
[Symbol.iterator]() {
132
return this;
133
}
134
135
/** @override */
136
next() {
137
while (true) {
138
const childResult = this.childIter_.next();
139
if (childResult.done) {
140
// Don't return childResult directly, because that would preserve
141
// generator return values, and we want to ignore them.
142
return {done: true, value: undefined};
143
}
144
const passesFilter = this.filterFn_.call(undefined, childResult.value);
145
if (passesFilter) {
146
return childResult;
147
}
148
}
149
}
150
}
151
152
153
/**
154
* Filter elements from one iterator to create another iterable.
155
*
156
* When next() is called on the returned iterator, it will call next() on the
157
* given iterator and call the given function `f` with that value until `true`
158
* is returned or the given iterator is exhausted.
159
*
160
* @param {!Iterable<VALUE>} iterable
161
* @param {function(VALUE): boolean} f
162
* @return {!IteratorIterable<VALUE>} The created iterable that gives the mapped
163
* values.
164
* @template VALUE
165
*/
166
exports.filter = function(iterable, f) {
167
return new FilterIterator(iterable, f);
168
};
169
170
171
/**
172
* @template T
173
* @implements {IteratorIterable<T>}
174
*/
175
class ConcatIterator {
176
/** @param {!Array<!Iterator<T>>} iterators */
177
constructor(iterators) {
178
/** @private @const {!Array<!Iterator<T>>} */
179
this.iterators_ = iterators;
180
181
/** @private {number} */
182
this.iterIndex_ = 0;
183
}
184
185
[Symbol.iterator]() {
186
return this;
187
}
188
189
/** @override */
190
next() {
191
while (this.iterIndex_ < this.iterators_.length) {
192
const result = this.iterators_[this.iterIndex_].next();
193
if (!result.done) {
194
return result;
195
}
196
this.iterIndex_++;
197
}
198
return /** @type {!IIterableResult<T>} */ ({done: true});
199
}
200
}
201
202
203
/**
204
* Concatenates multiple iterators to create a new iterable.
205
*
206
* When next() is called on the return iterator, it will call next() on the
207
* current passed iterator. When the current passed iterator is exhausted, it
208
* will move on to the next iterator until there are no more left.
209
*
210
* All generator return values will be ignored (i.e. when childIter.next()
211
* returns {done: true, value: notUndefined} it will be treated as just
212
* {done: true}).
213
*
214
* @param {...!Iterable<VALUE>} iterables
215
* @return {!IteratorIterable<VALUE>}
216
* @template VALUE
217
*/
218
exports.concat = function(...iterables) {
219
return new ConcatIterator(iterables.map(getIterator));
220
};
221
222
/**
223
* Creates an array containing the values from the given iterator.
224
* @param {!Iterator<VALUE>} iterator
225
* @return {!Array<VALUE>}
226
* @template VALUE
227
*/
228
exports.toArray = function(iterator) {
229
const arr = [];
230
forEach(iterator, e => arr.push(e));
231
return arr;
232
};
233
234