-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0bfd3d2
commit d052f2e
Showing
2 changed files
with
240 additions
and
1 deletion.
There are no files selected for viewing
239 changes: 239 additions & 0 deletions
239
0001-feat-array-support-iterables-in-some-array-functions.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
From 87c38ee9537a269cc3f8fe4da7990291f9edad0e Mon Sep 17 00:00:00 2001 | ||
From: Alec Larson <1925840+aleclarson@users.noreply.github.com> | ||
Date: Sat, 22 Jun 2024 14:34:09 -0400 | ||
Subject: [PATCH] feat(array): support iterables in some array functions | ||
|
||
--- | ||
src/array.ts | 115 +++++++++++++++++++++++++++++++++++---------------- | ||
1 file changed, 80 insertions(+), 35 deletions(-) | ||
|
||
diff --git a/src/array.ts b/src/array.ts | ||
index c7e70d4..f7e9cd9 100644 | ||
--- a/src/array.ts | ||
+++ b/src/array.ts | ||
@@ -6,10 +6,10 @@ import { isArray, isFunction } from './typed' | ||
* each item in that group. | ||
*/ | ||
export const group = <T, Key extends string | number | symbol>( | ||
- array: readonly T[], | ||
+ array: Iterable<T>, | ||
getGroupId: (item: T) => Key | ||
): Partial<Record<Key, T[]>> => { | ||
- return array.reduce((acc, item) => { | ||
+ return reduce(array, (acc, item) => { | ||
const groupId = getGroupId(item) | ||
if (!acc[groupId]) acc[groupId] = [] | ||
acc[groupId].push(item) | ||
@@ -58,10 +58,10 @@ export function zip<T>(...arrays: T[][]): T[][] { | ||
* Ex. const zipped = zipToObject(['a', 'b'], 1) // { a: 1, b: 1 } | ||
*/ | ||
export function zipToObject<K extends string | number | symbol, V>( | ||
- keys: K[], | ||
+ keys: Iterable<K>, | ||
values: V | ((key: K, idx: number) => V) | V[] | ||
): Record<K, V> { | ||
- if (!keys.length) { | ||
+ if (isEmpty(keys)) { | ||
return {} as Record<K, V> | ||
} | ||
|
||
@@ -71,7 +71,7 @@ export function zipToObject<K extends string | number | symbol, V>( | ||
? (_k: K, i: number) => values[i] | ||
: (_k: K, _i: number) => values | ||
|
||
- return keys.reduce((acc, key, idx) => { | ||
+ return reduce(keys, (acc, key, idx) => { | ||
acc[key] = getValue(key, idx) | ||
return acc | ||
}, {} as Record<K, V>) | ||
@@ -82,30 +82,37 @@ export function zipToObject<K extends string | number | symbol, V>( | ||
* and comparing with the second. Keep the one you want then | ||
* compare that to the next item in the list with the same | ||
* | ||
- * Ex. const greatest = () => boil(numbers, (a, b) => a > b) | ||
+ * Ex. const greatest = () => boil(numbers, (a, b) => a > b ? a : b) | ||
*/ | ||
-export const boil = <T>( | ||
- array: readonly T[], | ||
+export function boil<T>(array: readonly [T, ...T[]], compareFunc: (a: T, b: T) => T): T | ||
+export function boil<T>(array: Iterable<T>, compareFunc: (a: T, b: T) => T): T | null | ||
+export function boil <T>( | ||
+ array: Iterable<T>, | ||
compareFunc: (a: T, b: T) => T | ||
-) => { | ||
- if (!array || (array.length ?? 0) === 0) return null | ||
- return array.reduce(compareFunc) | ||
+) { | ||
+ if (isEmpty(array)) return null | ||
+ let acc!: T, i = 0 | ||
+ for (const item of array) { | ||
+ acc = i++ === 0 ? item : compareFunc(acc, item) | ||
+ } | ||
+ return acc | ||
} | ||
|
||
/** | ||
* Sum all numbers in an array. Optionally provide a function | ||
* to convert objects in the array to number values. | ||
*/ | ||
-export function sum<T extends number>(array: readonly T[]): number | ||
-export function sum<T extends object>( | ||
- array: readonly T[], | ||
- fn: (item: T) => number | ||
+export function sum<T extends number>(array: Iterable<T>): number | ||
+export function sum<T>( | ||
+ array: Iterable<T>, | ||
+ getter: (item: T) => number | ||
): number | ||
-export function sum<T extends object | number>( | ||
- array: readonly any[], | ||
- fn?: (item: T) => number | ||
+export function sum<T>( | ||
+ array: Iterable<any>, | ||
+ getter?: (item: T) => number | ||
): number { | ||
- return (array || []).reduce((acc, item) => acc + (fn ? fn(item) : item), 0) | ||
+ const get = getter ?? ((v: any) => v) | ||
+ return reduce(array, (acc, item) => acc + get(item), 0) | ||
} | ||
|
||
/** | ||
@@ -160,10 +167,10 @@ export const alphabetical = <T>( | ||
} | ||
|
||
export const counting = <T, TId extends string | number | symbol>( | ||
- list: readonly T[], | ||
+ list: Iterable<T>, | ||
identity: (item: T) => TId | ||
): Record<TId, number> => { | ||
- return list.reduce((acc, item) => { | ||
+ return reduce(list, (acc, item) => { | ||
const id = identity(item) | ||
acc[id] = (acc[id] ?? 0) + 1 | ||
return acc | ||
@@ -198,11 +205,11 @@ export const replace = <T>( | ||
* into a dictionary key & value | ||
*/ | ||
export const objectify = <T, Key extends string | number | symbol, Value = T>( | ||
- array: readonly T[], | ||
+ array: Iterable<T>, | ||
getKey: (item: T) => Key, | ||
getValue: (item: T) => Value = item => item as unknown as Value | ||
): Record<Key, Value> => { | ||
- return array.reduce((acc, item) => { | ||
+ return reduce(array, (acc, item) => { | ||
acc[getKey(item)] = getValue(item) | ||
return acc | ||
}, {} as Record<Key, Value>) | ||
@@ -216,11 +223,11 @@ export const objectify = <T, Key extends string | number | symbol, Value = T>( | ||
* select([1, 2, 3, 4], x => x*x, x > 2) == [9, 16] | ||
*/ | ||
export const select = <T, K>( | ||
- array: readonly T[], | ||
+ array: Iterable<T>, | ||
mapper: (item: T, index: number) => K, | ||
condition: (item: T, index: number) => boolean | ||
) => { | ||
- return array.reduce((acc, item, index) => { | ||
+ return reduce(array, (acc, item, index) => { | ||
if (!condition(item, index)) return acc | ||
acc.push(mapper(item, index)) | ||
return acc | ||
@@ -235,15 +242,19 @@ export const select = <T, K>( | ||
* max([{ num: 1 }, { num: 2 }], x => x.num) == { num: 2 } | ||
*/ | ||
export function max(array: readonly [number, ...number[]]): number | ||
-export function max(array: readonly number[]): number | null | ||
+export function max(array: Iterable<number>): number | null | ||
export function max<T>( | ||
- array: readonly T[], | ||
+ array: readonly [T, ...T[]], | ||
+ getter: (item: T) => number | ||
+): T | ||
+export function max<T>( | ||
+ array: Iterable<T>, | ||
getter: (item: T) => number | ||
): T | null | ||
export function max<T>( | ||
- array: readonly T[], | ||
+ array: Iterable<T>, | ||
getter?: (item: T) => number | ||
-): T | null { | ||
+) { | ||
const get = getter ?? ((v: any) => v) | ||
return boil(array, (a, b) => (get(a) > get(b) ? a : b)) | ||
} | ||
@@ -256,15 +267,19 @@ export function max<T>( | ||
* min([{ num: 1 }, { num: 2 }], x => x.num) == { num: 1 } | ||
*/ | ||
export function min(array: readonly [number, ...number[]]): number | ||
-export function min(array: readonly number[]): number | null | ||
+export function min(array: Iterable<number>): number | null | ||
export function min<T>( | ||
- array: readonly T[], | ||
+ array: readonly [T, ...T[]], | ||
+ getter: (item: T) => number | ||
+): T | ||
+export function min<T>( | ||
+ array: Iterable<T>, | ||
getter: (item: T) => number | ||
): T | null | ||
export function min<T>( | ||
- array: readonly T[], | ||
+ array: Iterable<T>, | ||
getter?: (item: T) => number | ||
-): T | null { | ||
+) { | ||
const get = getter ?? ((v: any) => v) | ||
return boil(array, (a, b) => (get(a) < get(b) ? a : b)) | ||
} | ||
@@ -288,10 +303,10 @@ export const cluster = <T>(list: readonly T[], size: number = 2): T[][] => { | ||
* value | ||
*/ | ||
export const unique = <T, K extends string | number | symbol>( | ||
- array: readonly T[], | ||
+ array: Iterable<T>, | ||
toKey?: (item: T) => K | ||
): T[] => { | ||
- const valueMap = array.reduce((acc, item) => { | ||
+ const valueMap = reduce(array, (acc, item) => { | ||
const key = toKey ? toKey(item) : (item as any as string | number | symbol) | ||
if (acc[key]) return acc | ||
acc[key] = item | ||
@@ -534,3 +549,33 @@ export function shift<T>(arr: Array<T>, n: number) { | ||
|
||
return [...arr.slice(-shiftNumber, arr.length), ...arr.slice(0, -shiftNumber)] | ||
} | ||
+ | ||
+function reduce<T, K>( | ||
+ iterable: Iterable<T>, | ||
+ reducer: (acc: K | undefined, item: T, index: number) => K, | ||
+): K | ||
+function reduce<T, K>( | ||
+ iterable: Iterable<T>, | ||
+ reducer: (acc: K, item: T, index: number) => K, | ||
+ initial: K | ||
+): K | ||
+function reduce<T, K>( | ||
+ iterable: Iterable<T>, | ||
+ reducer: (acc: K | undefined, item: T, index: number) => K, | ||
+ initial?: K, | ||
+) { | ||
+ let acc = initial | ||
+ let index = 0 | ||
+ for (const item of iterable) { | ||
+ acc = reducer(acc, item, index) | ||
+ index++ | ||
+ } | ||
+ return acc | ||
+} | ||
+ | ||
+ | ||
+function isEmpty(iterable: Iterable<any> | readonly any[]) { | ||
+ if (isArray(iterable)) return !iterable.length | ||
+ for (const _ of iterable) return false | ||
+ return true | ||
+} | ||
-- | ||
2.41.0 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters