UNPKG

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
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); }