initial commit

This commit is contained in:
2026-06-25 21:30:32 +00:00
commit 328faf6251
220 changed files with 162103 additions and 0 deletions
+222
View File
@@ -0,0 +1,222 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Utilities for working with ES6 iterables.
*
* The goal is that this should be a replacement for goog.iter which uses
* a now non-standard approach to iterables.
*
* @see https://goo.gl/Rok5YQ
*/
goog.module('goog.collections.iters');
goog.module.declareLegacyNamespace();
/**
* Get the iterator for an iterable.
* @param {!Iterable<VALUE>} iterable
* @return {!Iterator<VALUE>}
* @template VALUE
*/
function getIterator(iterable) {
return iterable[goog.global.Symbol.iterator]();
}
exports.getIterator = getIterator;
/**
* Call a function with every value of an iterable.
*
* Warning: this function will never halt if given an iterable that
* is never exhausted.
*
* @param {!Iterable<VALUE>} iterable
* @param {function(VALUE) : *} f
* @template VALUE
*/
exports.forEach = function(iterable, f) {
for (const elem of iterable) {
f(elem);
}
};
/**
* An Iterable that wraps a child iterable, and maps every element of the child
* iterator to a new value, using a mapping function. Similar to Array.map, but
* for Iterable.
* @template TO,FROM
* @implements {IteratorIterable<TO>}
*/
class MapIterator {
/**
* @param {!Iterable<FROM>} childIter
* @param {function(FROM, number): TO} mapFn
*/
constructor(childIter, mapFn) {
/** @private @const {!Iterator<FROM>} */
this.childIterator_ = getIterator(childIter);
/** @private @const {function(FROM, number): TO} */
this.mapFn_ = mapFn;
/** @private {number} */
this.nextIndex_ = 0;
}
[Symbol.iterator]() {
return this;
}
/** @override */
next() {
const childResult = this.childIterator_.next();
// Always return a new object, even when childResult.done == true. This is
// so that we don't accidentally preserve generator return values, which
// are unlikely to be meaningful in the context of this MapIterator.
return {
value: childResult.done ?
undefined :
this.mapFn_.call(undefined, childResult.value, this.nextIndex_++),
done: childResult.done,
};
}
}
/**
* Maps the values of one iterable to create another iterable.
*
* When next() is called on the returned iterable, it will call the given
* function `f` with the next value of the given iterable
* `iterable` until the given iterable is exhausted.
*
* @param {!Iterable<VALUE>} iterable
* @param {function(VALUE, number): RESULT} f
* @return {!IteratorIterable<RESULT>} The created iterable that gives the
* mapped values.
* @template VALUE, RESULT
*/
exports.map = function(iterable, f) {
return new MapIterator(iterable, f);
};
/**
* An Iterable that wraps a child Iterable and returns a subset of the child's
* items, based on a filter function. Similar to Array.filter, but for
* Iterable.
* @template T
* @implements {IteratorIterable<T>}
*/
class FilterIterator {
/**
* @param {!Iterable<T>} childIter
* @param {function(T, number): boolean} filterFn
*/
constructor(childIter, filterFn) {
/** @private @const {!Iterator<T>} */
this.childIter_ = getIterator(childIter);
/** @private @const {function(T, number): boolean} */
this.filterFn_ = filterFn;
/** @private {number} */
this.nextIndex_ = 0;
}
[Symbol.iterator]() {
return this;
}
/** @override */
next() {
while (true) {
const childResult = this.childIter_.next();
if (childResult.done) {
// Don't return childResult directly, because that would preserve
// generator return values, and we want to ignore them.
return {done: true, value: undefined};
}
const passesFilter =
this.filterFn_.call(undefined, childResult.value, this.nextIndex_++);
if (passesFilter) {
return childResult;
}
}
}
}
/**
* Filter elements from one iterator to create another iterable.
*
* When next() is called on the returned iterator, it will call next() on the
* given iterator and call the given function `f` with that value until `true`
* is returned or the given iterator is exhausted.
*
* @param {!Iterable<VALUE>} iterable
* @param {function(VALUE, number): boolean} f
* @return {!IteratorIterable<VALUE>} The created iterable that gives the mapped
* values.
* @template VALUE
*/
exports.filter = function(iterable, f) {
return new FilterIterator(iterable, f);
};
/**
* @template T
* @implements {IteratorIterable<T>}
*/
class ConcatIterator {
/** @param {!Array<!Iterator<T>>} iterators */
constructor(iterators) {
/** @private @const {!Array<!Iterator<T>>} */
this.iterators_ = iterators;
/** @private {number} */
this.iterIndex_ = 0;
}
[Symbol.iterator]() {
return this;
}
/** @override */
next() {
while (this.iterIndex_ < this.iterators_.length) {
const result = this.iterators_[this.iterIndex_].next();
if (!result.done) {
return result;
}
this.iterIndex_++;
}
return /** @type {!IIterableResult<T>} */ ({done: true});
}
}
/**
* Concatenates multiple iterators to create a new iterable.
*
* When next() is called on the return iterator, it will call next() on the
* current passed iterator. When the current passed iterator is exhausted, it
* will move on to the next iterator until there are no more left.
*
* All generator return values will be ignored (i.e. when childIter.next()
* returns {done: true, value: notUndefined} it will be treated as just
* {done: true}).
*
* @param {...!Iterable<VALUE>} iterables
* @return {!IteratorIterable<VALUE>}
* @template VALUE
*/
exports.concat = function(...iterables) {
return new ConcatIterator(iterables.map(getIterator));
};
+159
View File
@@ -0,0 +1,159 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Helper methods that operate on Map-like objects (e.g. ES6
* Maps).
*/
goog.module('goog.collections.maps');
goog.module.declareLegacyNamespace();
/**
* A MapLike implements the same public interface as an ES6 Map, without tying
* the underlying code directly to the implementation. Any additions to this
* type should also be present on ES6 Maps.
* @template K,V
* @record
*/
class MapLike {
constructor() {
/** @const {number} The number of items in this map. */
this.size;
}
/**
* @param {K} key The key to set in the map.
* @param {V} val The value to set for the given key in the map.
*/
set(key, val) {};
/**
* @param {K} key The key to retrieve from the map.
* @return {V|undefined} The value for this key, or undefined if the key is
* not present in the map.
*/
get(key) {};
/**
* @return {!IteratorIterable<K>} An ES6 Iterator that iterates over the keys
* in the map.
*/
keys() {};
/**
* @return {!IteratorIterable<V>} An ES6 Iterator that iterates over the
* values in the map.
*/
values() {};
/**
* @param {K} key The key to check.
* @return {boolean} True iff this key is present in the map.
*/
has(key) {};
}
exports.MapLike = MapLike;
/**
* Iterates over each entry in the given entries and sets the entry in
* the map, overwriting any existing entries for the key.
* @param {!MapLike<K,V>} map The map to set entries on.
* @param {?Iterable<!Array<K|V>>} entries The iterable of entries. This
* iterable should really be of type Iterable<Array<[K,V]>>, but the tuple
* type is not representable in the Closure Type System.
* @template K,V
*/
function setAll(map, entries) {
if (!entries) return;
for (const [k, v] of entries) {
map.set(k, v);
}
}
exports.setAll = setAll;
/**
* Determines if a given map contains the given value, optionally using
* a custom comparison function.
* @param {!MapLike<?,V1>} map The map whose values to check.
* @param {V2} val The value to check for.
* @param {(function(V1,V2): boolean)=} valueEqualityFn The comparison function
* used to determine if the given value is equivalent to any of the values
* in the map. If no function is provided, defaults to strict equality
* (===).
* @return {boolean} True iff the given map contains the given value according
* to the comparison function.
* @template V1,V2
*/
function hasValue(map, val, valueEqualityFn = defaultEqualityFn) {
for (const v of map.values()) {
if (valueEqualityFn(v, val)) return true;
}
return false;
}
exports.hasValue = hasValue;
/** @const {function(?,?): boolean} */
const defaultEqualityFn = (a, b) => a === b;
/**
* Compares two maps using their public APIs to determine if they have
* equal contents, optionally using a custom comparison function when comaring
* values.
* @param {!MapLike<K,V1>} map The first map
* @param {!MapLike<K,V2>} otherMap The other map
* @param {(function(V1,V2): boolean)=} valueEqualityFn The comparison function
* used to determine if the values obtained from each map are equivalent. If
* no function is provided, defaults to strict equality (===).
* @return {boolean}
* @template K,V1,V2
*/
function equals(map, otherMap, valueEqualityFn = defaultEqualityFn) {
if (map === otherMap) return true;
if (map.size !== otherMap.size) return false;
for (const key of map.keys()) {
if (!otherMap.has(key)) return false;
if (!valueEqualityFn(map.get(key), otherMap.get(key))) return false;
}
return true;
}
exports.equals = equals;
/**
* Returns a new ES6 Map in which all the keys and values from the
* given map are interchanged (keys become values and values become keys). If
* multiple keys in the given map to the same value, the resulting value in the
* transposed map is implementation-dependent.
*
* It acts very similarly to {goog.object.transpose(Object)}.
* @param {!MapLike<K,V>} map The map to transpose.
* @return {!Map<V,K>} A transposed version of the given map.
* @template K,V
*/
function transpose(map) {
const /** !Map<V,K> */ transposed = new Map();
for (const key of map.keys()) {
const val = map.get(key);
transposed.set(val, key);
}
return transposed;
}
exports.transpose = transpose;
/**
* ToObject returns a new object whose properties are the keys from the Map.
* @param {!MapLike<K,V>} map The map to convert into an object.
* @return {!Object<K,V>} An object representation of the Map.
* @template K,V
*/
function toObject(map) {
const /** !Object<K,V> */ obj = {};
for (const key of map.keys()) {
obj[key] = map.get(key);
}
return obj;
}
exports.toObject = toObject;