itertools-ts
Version:
Extended itertools port for TypeScript and JavaScript. Provides a huge set of functions for working with iterable collections (including async ones)
353 lines (315 loc) • 8.89 kB
text/typescript
import {
toAsyncIterable,
toIterable,
} from "./transform";
import {
createAsyncMultipleIterator,
createMultipleIterator,
MultipleIterationMode,
NoValueMonad,
UsageMap,
} from "./tools";
import { enumerate, } from "./single";
import { combinatorics, Comparable, single, ZipTuple } from "./index";
/**
* Iterate only the distinct elements.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param data
* @param compareBy
*/
export function* distinct<T>(
data: Iterable<T> | Iterator<T>,
compareBy?: (datum: T) => Comparable
): Iterable<T> {
const used = new Set();
if (data instanceof Map) {
if (compareBy === undefined) {
compareBy = (datum: T) => (datum as [unknown, Comparable])[1];
}
for (const datum of data) {
const comparable = compareBy(datum as T);
if (!used.has(comparable)) {
yield datum as T;
used.add(comparable);
}
}
} else {
if (compareBy === undefined) {
compareBy = (datum: T) => datum as Comparable;
}
for (const datum of toIterable(data)) {
const comparable = compareBy(datum);
if (!used.has(comparable)) {
yield datum;
used.add(comparable);
}
}
}
}
/**
* Iterate only the distinct elements from async collection.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param data
* @param compareBy
*/
export async function* distinctAsync<T>(
data: AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>,
compareBy?: (datum: T) => Comparable
): AsyncIterable<T> {
const used = new Set();
if (data instanceof Map) {
for (const datum of distinct(data as Iterable<T>, compareBy)) {
yield await datum;
}
} else {
if (compareBy === undefined) {
compareBy = (datum: T) => datum as Comparable;
}
for await (const datum of toAsyncIterable(data)) {
const comparable = compareBy(datum);
if (!used.has(comparable)) {
yield datum;
used.add(comparable);
}
}
}
}
/**
* Iterates the intersection of iterables using type coercion.
*
* If input iterables produce duplicate items, then multiset intersection rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param iterables
*/
export function* intersection<T>(
...iterables: Array<Iterable<T> | Iterator<T>>
): Iterable<T> {
yield* partialIntersection(iterables.length, ...iterables);
}
/**
* Iterates the intersection of async iterables using type coercion.
*
* If input iterables produce duplicate items, then multiset intersection rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param iterables
*/
export async function* intersectionAsync<T>(
...iterables: Array<
AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>
>
): AsyncIterable<T> {
yield* partialIntersectionAsync(iterables.length, ...iterables);
}
/**
* Iterates partial intersection of iterables.
*
* If input iterables produce duplicate items, then multiset intersection rules apply.
* If `minIntersectionCount` is 1, then multiset union rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param minIntersectionCount
* @param iterables
*/
export function* partialIntersection<T>(
minIntersectionCount: number,
...iterables: Array<Iterable<T> | Iterator<T>>
): Iterable<T> {
const usageMap = new UsageMap();
const multipleIterator = createMultipleIterator(
MultipleIterationMode.LONGEST,
NoValueMonad,
...iterables
);
for (const values of multipleIterator) {
for (const [owner, value] of enumerate(values)) {
if (value === NoValueMonad) {
continue;
}
usageMap.addUsage(value, `${owner}`);
if (usageMap.getOwnersCount(value) === minIntersectionCount) {
yield value as T;
usageMap.deleteUsage(value);
}
}
}
}
/**
* Iterates partial intersection of async iterables.
*
* If input iterables produce duplicate items, then multiset intersection rules apply.
* If `minIntersectionCount` is 1, then multiset union rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param minIntersectionCount
* @param iterables
*/
export async function* partialIntersectionAsync<T>(
minIntersectionCount: number,
...iterables: Array<
AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>
>
): AsyncIterable<T> {
const usageMap = new UsageMap();
const multipleIterator = createAsyncMultipleIterator(
MultipleIterationMode.LONGEST,
NoValueMonad,
...iterables
);
for await (const values of multipleIterator) {
for (const [owner, value] of enumerate(values)) {
if (value === NoValueMonad) {
continue;
}
usageMap.addUsage(value, `${owner}`);
if (usageMap.getOwnersCount(value) === minIntersectionCount) {
yield value as T;
usageMap.deleteUsage(value);
}
}
}
}
/**
* Iterates the symmetric difference of iterables.
*
* If input iterables produce duplicate items, then multiset difference rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param iterables
*/
export function* symmetricDifference<T>(
...iterables: Array<Iterable<T> | Iterator<T>>
): Iterable<T> {
const usageMap = new UsageMap();
const valuesSet: Set<T> = new Set();
const multipleIterator = createMultipleIterator(
MultipleIterationMode.LONGEST,
NoValueMonad,
...iterables
);
for (const values of multipleIterator) {
for (const [owner, value] of enumerate(values)) {
if (value === NoValueMonad) {
continue;
}
usageMap.addUsage(value, `${owner}`);
valuesSet.add(value as T);
if (usageMap.getOwnersCount(value) === iterables.length) {
usageMap.deleteUsage(value);
}
}
}
for (const value of valuesSet) {
for (const item of single.repeat(value, usageMap.getUsagesCount(value))) {
yield item as T;
}
}
}
/**
* Iterates the symmetric difference of async iterables.
*
* If input iterables produce duplicate items, then multiset difference rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param iterables
*/
export async function* symmetricDifferenceAsync<T>(
...iterables: Array<
AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>
>
): AsyncIterable<T> {
const usageMap = new UsageMap();
const valuesSet: Set<T> = new Set();
const multipleIterator = createAsyncMultipleIterator(
MultipleIterationMode.LONGEST,
NoValueMonad,
...iterables
);
for await (const values of multipleIterator) {
for (const [owner, value] of enumerate(values)) {
if (value === NoValueMonad) {
continue;
}
usageMap.addUsage(value, `${owner}`);
valuesSet.add(value as T);
if (usageMap.getOwnersCount(value) === iterables.length) {
usageMap.deleteUsage(value);
}
}
}
for (const value of valuesSet) {
for (const item of single.repeat(value, usageMap.getUsagesCount(value))) {
yield item as T;
}
}
}
/**
* Iterates union of given iterables.
*
* If input iterables produce duplicate items, then multiset intersection rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param iterables
*/
export function* union<T>(
...iterables: Array<Iterable<T> | Iterator<T>>
): Iterable<T> {
yield* partialIntersection(1, ...iterables);
}
/**
* Iterates union of given async iterables.
*
* If input iterables produce duplicate items, then multiset intersection rules apply.
*
* Always treats different instances of objects and arrays as unequal.
*
* @param iterables
*/
export async function* unionAsync<T>(
...iterables: Array<
AsyncIterable<T> | AsyncIterator<T> | Iterable<T> | Iterator<T>
>
): AsyncIterable<T> {
yield* partialIntersectionAsync(1, ...iterables);
}
/**
* Iterates cartesian product of given iterables.
*
* @param iterables
*
* @deprecated Use `combinatorics.cartesianProduct()` instead.
*/
export function* cartesianProduct<
T extends Array<Iterable<unknown> | Iterator<unknown>>
>(...iterables: T): Iterable<ZipTuple<T, never>> {
yield* combinatorics.cartesianProduct(...iterables);
}
/**
* Iterates cartesian product of given async iterables.
*
* @param iterables
*
* @deprecated Use `combinatorics.cartesianProductAsync()` instead.
*/
export async function* cartesianProductAsync<
T extends Array<
| AsyncIterable<unknown>
| AsyncIterator<unknown>
| Iterable<unknown>
| Iterator<unknown>
>
>(...iterables: T): AsyncIterable<ZipTuple<T, never>> {
yield* combinatorics.cartesianProductAsync(...iterables);
}