itertools-ts
Version:
Extended itertools port for TypeScript and JavaScript. Provides a huge set of functions for working with iterable collections (including async ones)
293 lines (269 loc) • 7.11 kB
text/typescript
import { InvalidArgumentError } from "./exceptions";
import { isAsyncIterable, isIterable, isIterator } from "./summary";
import { RecordKey } from "./types";
import {
AsyncRelatedIterable,
AsyncTeeIterator,
RelatedIterable,
TeeIterator,
} from "./tools";
/**
* Converts collection or record to Iterable instance.
*
* If instance is already an iterable then just return it.
*
* @param collection
*/
export function toIterable<T>(
collection: Iterable<T> | Iterator<T> | Record<RecordKey, unknown>
): Iterable<T> {
if (isIterable(collection)) {
return collection as Iterable<T>;
}
if (isIterator(collection)) {
return {
[Symbol.iterator](): Iterator<T> {
return collection as Iterator<T>;
},
};
}
if (typeof collection === "object" && collection !== null) {
return (function* () {
for (const key in collection) {
if (Object.prototype.hasOwnProperty.call(collection, key)) {
yield [
key,
(collection as Record<string | number | symbol, unknown>)[key],
];
}
}
})() as Iterable<T>;
}
throw new InvalidArgumentError(
"Given collection is not iterable or iterator."
);
}
/**
* Converts collection or record to AsyncIterable instance.
*
* If instance is already an async iterable then just return it.
*
* @param collection
*/
export function toAsyncIterable<T>(
collection:
| Iterable<T>
| Iterator<T>
| AsyncIterable<T>
| AsyncIterator<T>
| Record<RecordKey, unknown>
): AsyncIterable<T> {
if (isAsyncIterable(collection)) {
return collection as AsyncIterable<T>;
}
if (isIterator(collection)) {
return {
async *[Symbol.asyncIterator]() {
while (true) {
const res = await (collection as AsyncIterator<T>).next();
if (res.done) {
return;
}
yield res.value;
}
},
};
}
if (typeof collection === "object" && collection !== null) {
collection = toIterable(collection as Record<RecordKey, unknown>);
}
if (isIterable(collection)) {
return {
async *[Symbol.asyncIterator]() {
for (const value of collection as Iterable<T>) {
yield value;
}
},
};
}
throw new InvalidArgumentError(
"Given collection is not async iterable or iterator."
);
}
/**
* Converts collection to Iterator instance.
*
* If instance is already an iterator then just return it.
*
* @param collection
*/
export function toIterator<T>(
collection: Iterable<T> | Iterator<T>
): Iterator<T> {
if (isIterator(collection)) {
return collection as Iterator<T>;
}
if (isIterable(collection)) {
return (function* () {
for (const item of collection as Iterable<T>) {
yield item;
}
})();
}
throw new InvalidArgumentError(
"Given collection is not iterable or iterator."
);
}
/**
* Converts collection to AsyncIterator instance.
*
* @param collection
*/
export function toAsyncIterator<T>(
collection: Iterable<T> | Iterator<T> | AsyncIterable<T> | AsyncIterator<T>
): AsyncIterator<T> {
if (isIterator(collection) || isIterable(collection)) {
collection = toAsyncIterable(collection);
}
if (isAsyncIterable(collection)) {
return (async function* () {
for await (const item of collection as Iterable<T>) {
yield item;
}
})();
}
throw new InvalidArgumentError(
"Given collection is not iterable or iterator."
);
}
/**
* Converts given collection to array.
*
* @param collection
*/
export function toArray<T>(collection: Iterable<T> | Iterator<T>): Array<T> {
if (Array.isArray(collection)) {
return collection;
}
const result = [];
for (const item of toIterable(collection)) {
result.push(item);
}
return result;
}
/**
* Converts given async collection to array.
*
* @param collection
*/
export async function toArrayAsync<T>(
collection: AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>
): Promise<Array<T>> {
if (Array.isArray(collection)) {
return collection;
}
const result = [];
for await (const item of toAsyncIterable(collection)) {
result.push(item);
}
return result;
}
/**
* Converts collection of key-value pairs to Map.
*
* @param pairs
*/
export function toMap<TKey, TValue>(
pairs:
| Iterable<[TKey, TValue]>
| Iterator<[TKey, TValue]>
| Record<RecordKey, unknown>
): Map<TKey, TValue> {
const result: Map<TKey, TValue> = new Map();
for (const [key, value] of toIterable(pairs)) {
result.set(key, value);
}
return result;
}
/**
* Converts async collection of key-value pairs to Map.
*
* @param pairs
*/
export async function toMapAsync<TKey, TValue>(
pairs:
| AsyncIterable<[TKey, TValue]>
| AsyncIterator<[TKey, TValue]>
| Iterable<[TKey, TValue]>
| Iterator<[TKey, TValue]>
| Record<RecordKey, unknown>
): Promise<Map<TKey, TValue>> {
const result: Map<TKey, TValue> = new Map();
for await (const [key, value] of toAsyncIterable(pairs)) {
result.set(key, value);
}
return result;
}
/**
* Converts given collection to Set.
*
* @param collection
*/
export function toSet<T>(collection: Iterable<T> | Iterator<T>): Set<T> {
const result: Set<T> = new Set();
for (const datum of toIterable(collection)) {
result.add(datum);
}
return result;
}
/**
* Converts given async collection to Set.
*
* @param collection
*/
export async function toSetAsync<T>(
collection: AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>
): Promise<Set<T>> {
const result: Set<T> = new Set();
for await (const datum of toAsyncIterable(collection)) {
result.add(datum);
}
return result;
}
/**
* Return several independent (duplicated) iterables from a single iterable.
*
* Once tee has been called to duplicate iterators, it is advisable to not use the original input iterator any further.
*
* Duplicating iterators can use up memory. Consider if tee is the right solution. For example, arrays and most
* iterators can be rewound and reiterated without need for duplication.
*
* @param collection
* @param count
*/
export function tee<T>(
collection: Iterable<T> | Iterator<T>,
count: number
): Array<RelatedIterable<T>> {
return new TeeIterator(toIterator(collection), count).getRelatedIterables();
}
/**
* Return several independent (duplicated) async iterables from a single async iterable.
*
* Once tee has been called to duplicate iterators, it is advisable to not use the original input iterator any further.
*
* Duplicating iterators can use up memory. Consider if tee is the right solution. For example, arrays and most
* iterators can be rewound and reiterated without need for duplication.
*
* @param collection
* @param count
*/
export function teeAsync<T>(
collection: AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>,
count: number
): Array<AsyncRelatedIterable<T>> {
return new AsyncTeeIterator(
toAsyncIterator(collection),
count
).getRelatedIterables();
}