Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
"@types/benchmark": "^2.1.5",
"@vitest/coverage-v8": "^3.2.4",
"benchmark": "^2.1.4",
"typescript": "^5",
"vitest": "^3.2.4",
},
"peerDependencies": {
"typescript": "^5",
},
},
},
"packages": {
Expand Down Expand Up @@ -310,6 +314,8 @@

"tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],

"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],

"vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="],

"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
Expand Down
97 changes: 96 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,24 @@ declare module 'fluent-iter' {
select<TOutput>(map: (item: TValue) => TOutput): FluentIterable<TOutput>;

/**
* Flat Iterable of collections
* Flat Iterable of collections up to N levels, default is 1 level.
* @param depth
*/
flat(depth?: number): FlatFluentIterable<TValue>;

/**
* Flat Iterable of collections, alias of flat
* @param innerSelector Function which returns an inner collection
*/
selectMany<TInner>(innerSelector: (item: TValue) => TInner[]): FluentIterable<TInner>;

/**
* Flat iterable of collection one level and maps inner elements.
* @param innerSelector Function which returns an inner collection
* @param mapper Function thish converts inner element to result.
*/
flatMap<TResult>(mapper: (value: TValue) => TResult | ReadonlyArray<TResult>): FluentIterable<TResult>;

/**
* Flat iterable of collection
* @param innerSelector Function which returns an inner collection
Expand Down Expand Up @@ -447,13 +460,92 @@ declare module 'fluent-iter' {
*/
take(count: number): FluentIterableAsync<TValue>;

/**
* Return items while condition return true
* @param condition
*/
takeWhile(condition: (item: TValue, index: number) => boolean): FluentIterableAsync<TValue>;

/**
* Skip first N items from iterable
* @param count
*/
skip(count: number): FluentIterableAsync<TValue>;

/**
* Skip items while condition return true, get the rest
* @param condition
*/
skipWhile(condition: (item: TValue, index: number) => boolean): FluentIterableAsync<TValue>;

/**
* Return distinct items. Can specify optional item comparer
* @param keySelector function to get key for comparison.
*/
distinct<TKey>(keySelector?: (item: TValue) => TKey): FluentIterableAsync<TValue>;

/**
* Group items
* @param keySelector group key selector
*/
groupBy<TKey>(keySelector: (item: TValue, index: number) => TKey):
[TKey, TValue] extends ['fulfilled' | 'rejected', PromiseResult<infer TPromiseValue>]
? FluentIterableAsync< IGrouping<'fulfilled', FulfilledPromiseResult<TPromiseValue>> | IGrouping<'rejected', RejectedPromiseResult>>
: FluentIterableAsync<IGrouping<TKey, TValue>>;
groupBy<TKey, TElement>(keySelector: (item: TValue, index: number) => TKey, elementSelector: (item: TValue, index: number) => TElement): FluentIterableAsync<IGrouping<TKey, TElement>>;
groupBy<TKey, TElement, TResult>(keySelector: (item: TValue, index: number) => TKey, elementSelector: (item: TValue, index: number) => TElement, resultCreator: (key: TKey, items: FluentIterable<TElement>) => TResult): FluentIterableAsync<TResult>;

/**
* Return a promise to an array.
*/
toArray(): Promise<TValue[]>;
toArray<TResult>(map: (item: TValue) => TResult): Promise<TResult[]>;

/**
* Create a map object from sequence
* @param keySelector - key selector - keys should be unique, otherwise last keys will override first.
*/
toMap<TKey>(keySelector: (item: TValue) => TKey):
[TKey, TValue] extends ['fulfilled' | 'rejected', IGrouping<'fulfilled', FulfilledPromiseResult<infer TPromiseValue>> | IGrouping<'rejected', RejectedPromiseResult>]
? Promise<PromiseMap<TPromiseValue>>
: Promise<Map<TKey, TValue>>;

/**
* Create a map object from sequence
* @param keySelector key selector - keys should be unique, otherwise last keys will override first.
* @param elementSelector element selector
*/
toMap<TKey, TElement>(keySelector: (item: TValue) => TKey, elementSelector: (item: TValue) => TElement): Promise<Map<TKey, TElement>>;
}

export interface FluentIterableAsyncPromise<T> extends FluentIterableAsync<PromiseResult<T>> {
groupByStatus(): FluentIterableAsync< IGrouping<'fulfilled', FulfilledPromiseResult<T>> | IGrouping<'rejected', RejectedPromiseResult>>;
toStatusMap(): Promise<PromiseMap<T>>;
}

type PromiseResult<T> = FulfilledPromiseResult<T> | RejectedPromiseResult;

interface FulfilledPromiseResult<T> {
index: number;
status: 'fulfilled';
value: T;
}

interface RejectedPromiseResult {
index: number;
status: 'rejected';
reason: any;
}

interface PromiseMap<T> extends Map<'fulfilled'|'rejected', FluentIterable<PromiseResult<T>>> {
get(key: 'fulfilled'): FluentIterable<FulfilledPromiseResult<T>> | undefined;
get(key: 'rejected'): FluentIterable<RejectedPromiseResult> | undefined;
}

type FlatFluentIterable<Value> = Value extends ReadonlyArray<infer InnerArr>
? FlatFluentIterable<InnerArr>
: FluentIterable<Value>;

export function from<TValue>(iterable: Iterable<TValue> | ArrayLike<TValue>): FluentIterable<TValue>;
export function from<TValue extends {}, TKey extends keyof TValue>(value: TValue): FluentIterable<{ key: string, value: TValue[TKey] }>;

Expand All @@ -468,4 +560,7 @@ declare module 'fluent-iter' {

export function fromEvent<TTarget extends EventTarget, TEvent extends keyof HTMLElementEventMap>(target: TTarget, event: TEvent): FluentIterableAsync<HTMLElementEventMap[TEvent]>;
export function fromTimer(interval: number, delay?: number): FluentIterableAsync<number>;
export function fromPromises<T>(...promises: Promise<T>[]): FluentIterableAsyncPromise<T>;
export function isFulfilled<T>(result: PromiseResult<T>): result is FulfilledPromiseResult<T>;
export function isRejected<T>(result: PromiseResult<T>): result is RejectedPromiseResult;
}
3 changes: 3 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ export {
// async
fromEvent,
fromTimer,
fromPromises,
isFulfilled,
isRejected,
} from "./src/index.ts";
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"type": "module",
"types": "index.d.ts",
"scripts": {
"build": "vite build",
"check": "tsc --noEmit",
"test": "vitest --run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
Expand Down Expand Up @@ -45,7 +45,8 @@
"@types/benchmark": "^2.1.5",
"@vitest/coverage-v8": "^3.2.4",
"benchmark": "^2.1.4",
"vitest": "^3.2.4"
"vitest": "^3.2.4",
"typescript": "^5"
},
"peerDependencies": {
"typescript": "^5"
Expand Down
11 changes: 9 additions & 2 deletions src/creation-async.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { FluentIterableAsync } from 'fluent-iter';
import FluentAsync from "./fluent-async.js";
import type { FluentIterableAsync, FluentIterableAsyncPromise } from 'fluent-iter';
import FluentAsync, {FluentAsyncPromise} from "./fluent-async.js";
import fromEventAsync from "./generators/from-event.ts";
import fromTimerAsync from "./generators/from-timer.js";
import {fromPromisesIterable} from "./generators/promises.ts";

export function fromEvent<TTarget extends EventTarget, TEvent extends keyof HTMLElementEventMap>(target: TTarget, event: TEvent): FluentIterableAsync<HTMLElementEventMap[TEvent]> {
return new FluentAsync(fromEventAsync(target, event));
Expand All @@ -10,3 +11,9 @@ export function fromEvent<TTarget extends EventTarget, TEvent extends keyof HTML
export function fromTimer(interval: number, delay?: number): FluentIterableAsync<number> {
return new FluentAsync(fromTimerAsync(interval, delay));
}

export function fromPromises<T>(
...promises: Promise<T>[]
): FluentIterableAsyncPromise<T> {
return new FluentAsyncPromise(fromPromisesIterable(...promises));
}
24 changes: 18 additions & 6 deletions src/creation.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import type { FluentIterable } from 'fluent-iter';
import type { FluentIterable, FluentIterableAsync } from 'fluent-iter';
import Fluent from "./fluent.ts";
import arrayLikeIterator from "./iterables/initial/array-like.ts";
import objectIterator from "./iterables/initial/object.ts";
import arrayLikeIterator from "./generators/array-like.ts";
import objectIterator from "./generators/object.ts";
import rangeIterable from "./generators/range.ts";
import repeatIterable from "./generators/repeat.ts";
import FluentAsync from "./fluent-async.ts";

export function fromIterable<TValue>(iterable: Iterable<TValue>): FluentIterable<TValue> {
return new Fluent<TValue>(iterable);
}

export function fromAsyncIterable<TValue>(iterable: AsyncIterable<TValue>): FluentIterableAsync<TValue> {
return new FluentAsync<TValue>(iterable);
}

export function fromObject<TValue extends {}, TKey extends keyof TValue>(value: TValue): FluentIterable<{ key: string, value: TValue[TKey] }>;
export function fromObject<TValue extends {}, TKey extends keyof TValue, TResult>(value: TValue, resultCreator: (key: TKey, value: TValue[TKey]) => TResult): FluentIterable<TResult>;
export function fromObject<TValue extends {}, TKey extends keyof TValue, TResult>(value: TValue, resultCreator?: (key: TKey, value: TValue[TKey]) => TResult): FluentIterable<{ key: string, value: TValue[TKey] } | TResult> {
Expand All @@ -31,25 +36,32 @@ export function repeat<TValue>(value: TValue, times: number): FluentIterable<TVa
return new Fluent(repeatIterable(value, times));
}

export function from<TValue>(iterable: AsyncIterable<TValue>): FluentIterableAsync<TValue>;
export function from<TValue>(iterable: Iterable<TValue> | ArrayLike<TValue>): FluentIterable<TValue>;
export function from<TValue extends {}, TKey extends keyof TValue>(value: TValue): FluentIterable<{ key: string, value: TValue[TKey] }>;
export function from<TValue>(source: Iterable<TValue> | ArrayLike<TValue> | TValue) {
export function from<TValue>(source: Iterable<TValue> | ArrayLike<TValue> | TValue | AsyncIterable<TValue>) {
if (isIterable(source)) {
return fromIterable(source);
}
if (isAsyncIterable(source)) {
return fromAsyncIterable(source);
}
if (isArrayLike(source)) {
return fromArrayLike(source);
}
return fromObject(source as object);
}



function isIterable<T>(o: any): o is Iterable<T> {
const iterator = o[Symbol.iterator];
return typeof iterator === 'function';
}

function isAsyncIterable<T>(o: any): o is AsyncIterable<T> {
const iterator = o[Symbol.asyncIterator];
return typeof iterator === 'function';
}

function isArrayLike<T>(o: any): o is ArrayLike<T> {
return 'length' in o;
}
11 changes: 11 additions & 0 deletions src/finalizers/to-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,14 @@ export async function toArrayAsyncCollector<T, R>(source: AsyncIterable<T>, map?
}
return result;
}

export async function toMapAsyncCollector<TValue, TKey, TElement>(
source: AsyncIterable<TValue>,
keySelector: (item: TValue) => TKey,
elementSelector?: (item: TValue) => TElement): Promise<Map<TKey, TValue|TElement>> {
const map = new Map<TKey, TValue|TElement>();
for await (const item of source) {
map.set(keySelector(item), elementSelector ? elementSelector(item) : item);
}
return map;
}
Loading