From 0d44ea98e9c10ed5657b364c5f3ca55d9dea6403 Mon Sep 17 00:00:00 2001
From: haoziqaq <357229046@qq.com>
Date: Thu, 31 Oct 2024 16:10:40 +0800
Subject: [PATCH] feat: support more utils
---
docs/.vitepress/config.ts | 12 +++-
docs/.vitepress/items/array.ts | 2 -
docs/.vitepress/items/collection.ts | 5 +-
docs/.vitepress/items/function.ts | 3 +-
docs/.vitepress/items/index.ts | 1 +
docs/.vitepress/items/math.ts | 9 +++
docs/.vitepress/items/number.ts | 2 +
docs/collection/merge-with.md | 18 ++----
docs/collection/merge.md | 25 +++++++++
docs/function/call.md | 27 +++++----
docs/function/debounce.md | 14 ++---
docs/function/throttle.md | 14 ++---
docs/math/max-by.md | 25 +++++++++
docs/math/mean-by.md | 27 +++++++++
docs/math/mean.md | 24 ++++++++
docs/math/min-by.md | 25 +++++++++
docs/math/sample.md | 24 ++++++++
docs/{array => math}/sum-by.md | 2 +-
docs/{array => math}/sum.md | 0
docs/number/delay.md | 25 +++++++++
docs/number/times.md | 27 +++++++++
docs/zh/collection/merge-with.md | 23 +++-----
docs/zh/collection/merge.md | 25 +++++++++
docs/zh/function/call.md | 27 +++++----
docs/zh/function/debounce.md | 14 ++---
docs/zh/function/throttle.md | 14 ++---
docs/zh/math/max-by.md | 25 +++++++++
docs/zh/math/mean-by.md | 27 +++++++++
docs/zh/math/mean.md | 24 ++++++++
docs/zh/math/min-by.md | 25 +++++++++
docs/zh/math/sample.md | 24 ++++++++
docs/zh/math/sum-by.md | 24 ++++++++
docs/zh/math/sum.md | 23 ++++++++
docs/zh/number/delay.md | 25 +++++++++
docs/zh/number/times.md | 27 +++++++++
src/array.ts | 8 ---
src/collection.ts | 11 +++-
src/index.ts | 1 +
src/math.ts | 45 +++++++++++++++
src/number.ts | 8 +++
tests/array.spec.ts | 10 ----
tests/collection.spec.ts | 84 ++++++++++++++--------------
tests/math.spec.ts | 34 +++++++++++
tests/number.spec.ts | 87 ++++++++++++++++-------------
44 files changed, 738 insertions(+), 188 deletions(-)
create mode 100644 docs/.vitepress/items/math.ts
create mode 100644 docs/collection/merge.md
create mode 100644 docs/math/max-by.md
create mode 100644 docs/math/mean-by.md
create mode 100644 docs/math/mean.md
create mode 100644 docs/math/min-by.md
create mode 100644 docs/math/sample.md
rename docs/{array => math}/sum-by.md (85%)
rename docs/{array => math}/sum.md (100%)
create mode 100644 docs/number/delay.md
create mode 100644 docs/number/times.md
create mode 100644 docs/zh/collection/merge.md
create mode 100644 docs/zh/math/max-by.md
create mode 100644 docs/zh/math/mean-by.md
create mode 100644 docs/zh/math/mean.md
create mode 100644 docs/zh/math/min-by.md
create mode 100644 docs/zh/math/sample.md
create mode 100644 docs/zh/math/sum-by.md
create mode 100644 docs/zh/math/sum.md
create mode 100644 docs/zh/number/delay.md
create mode 100644 docs/zh/number/times.md
create mode 100644 src/math.ts
create mode 100644 tests/math.spec.ts
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 0c631ff..ec9d781 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -1,5 +1,5 @@
import { defineConfig } from 'vitepress'
-import { generalItems, numberItems, stringItems, arrayItems, functionItems, collectionItems } from './items'
+import { generalItems, numberItems, stringItems, arrayItems, functionItems, collectionItems, mathItems } from './items'
function withI18n(items: { link: string; text: string }[], locale: 'zh') {
return items.map((item) => {
@@ -43,6 +43,10 @@ export default defineConfig({
text: '字符串',
items: withI18n(stringItems, 'zh'),
},
+ {
+ text: '数学',
+ items: withI18n(mathItems, 'zh'),
+ },
{
text: '数组',
items: withI18n(arrayItems, 'zh'),
@@ -54,7 +58,7 @@ export default defineConfig({
{
text: '函数',
items: withI18n(functionItems, 'zh'),
- }
+ },
],
docFooter: {
@@ -93,6 +97,10 @@ export default defineConfig({
text: 'String',
items: stringItems,
},
+ {
+ text: 'Math',
+ items: mathItems,
+ },
{
text: 'Array',
items: arrayItems,
diff --git a/docs/.vitepress/items/array.ts b/docs/.vitepress/items/array.ts
index a491a12..f0c9567 100644
--- a/docs/.vitepress/items/array.ts
+++ b/docs/.vitepress/items/array.ts
@@ -1,7 +1,5 @@
export const arrayItems = [
{ text: 'at', link: '/array/at' },
- { text: 'sum', link: '/array/sum' },
- { text: 'sumBy', link: '/array/sum-by' },
{ text: 'uniq', link: '/array/uniq' },
{ text: 'uniqBy', link: '/array/uniq-by' },
{ text: 'find', link: '/array/find' },
diff --git a/docs/.vitepress/items/collection.ts b/docs/.vitepress/items/collection.ts
index accea49..3638c16 100644
--- a/docs/.vitepress/items/collection.ts
+++ b/docs/.vitepress/items/collection.ts
@@ -1 +1,4 @@
-export const collectionItems = [{ text: 'mergeWith', link: '/collection/merge-with' }]
+export const collectionItems = [
+ { text: 'merge', link: '/collection/merge' },
+ { text: 'mergeWith', link: '/collection/merge-with' },
+]
diff --git a/docs/.vitepress/items/function.ts b/docs/.vitepress/items/function.ts
index 867749b..3364563 100644
--- a/docs/.vitepress/items/function.ts
+++ b/docs/.vitepress/items/function.ts
@@ -1,5 +1,6 @@
export const functionItems = [
{ text: 'NOOP', link: '/function/noop' },
- { text: 'debounce', link: '/function/debounce' },
{ text: 'call', link: '/function/call' },
+ { text: 'debounce', link: '/function/debounce' },
+ { text: 'throttle', link: '/function/throttle' },
]
diff --git a/docs/.vitepress/items/index.ts b/docs/.vitepress/items/index.ts
index bf7175f..6dfbdf8 100644
--- a/docs/.vitepress/items/index.ts
+++ b/docs/.vitepress/items/index.ts
@@ -4,3 +4,4 @@ export * from './number'
export * from './array'
export * from './collection'
export * from './function'
+export * from './math'
diff --git a/docs/.vitepress/items/math.ts b/docs/.vitepress/items/math.ts
new file mode 100644
index 0000000..5fe82b6
--- /dev/null
+++ b/docs/.vitepress/items/math.ts
@@ -0,0 +1,9 @@
+export const mathItems = [
+ { text: 'sum', link: '/math/sum' },
+ { text: 'sumBy', link: '/math/sum-by' },
+ { text: 'minBy', link: '/math/min-by' },
+ { text: 'maxBy', link: '/math/max-by' },
+ { text: 'mean', link: '/math/mean' },
+ { text: 'meanBy', link: '/math/mean-by' },
+ { text: 'sample', link: '/math/sample' },
+]
diff --git a/docs/.vitepress/items/number.ts b/docs/.vitepress/items/number.ts
index 35690f8..3768117 100644
--- a/docs/.vitepress/items/number.ts
+++ b/docs/.vitepress/items/number.ts
@@ -4,4 +4,6 @@ export const numberItems = [
{ text: 'randomNumber', link: '/number/random-number' },
{ text: 'clamp', link: '/number/clamp' },
{ text: 'clampArrayRange', link: '/number/clamp-array-range' },
+ { text: 'times', link: '/number/times' },
+ { text: 'delay', link: '/number/delay' },
]
diff --git a/docs/collection/merge-with.md b/docs/collection/merge-with.md
index 89dac39..8f35be0 100644
--- a/docs/collection/merge-with.md
+++ b/docs/collection/merge-with.md
@@ -7,17 +7,11 @@ Merge two objects recursively, allowing for custom merge logic through a callbac
```ts
import { mergeWith } from 'rattail'
-const object = { a: 1, b: { c: 2 } }
-const source = { b: { d: 3 }, e: 4 }
-const result = mergeWith(object, source)
-// result: { a: 1, b: { c: 2, d: 3 }, e: 4 }
-```
-
-```ts
-const object = { a: [1, 2] }
-const source = { a: [3, 4] }
-const result = mergeWith(object, source, (objValue, srcValue) => [...objValue, ...srcValue])
-// result: { a: [ 1, 2, 3, 4 ] }
+mergeWith(
+ { a: [1, 2] }, { a: [3, 4] },
+ (objValue, srcValue) => [...objValue, ...srcValue]
+)
+// return: { a: [ 1, 2, 3, 4 ] }
```
### Arguments
@@ -26,7 +20,7 @@ const result = mergeWith(object, source, (objValue, srcValue) => [...objValue, .
| ---------- | ----------------------------------------------------------------------------------------- | -------- |
| `object` | `object` | |
| `source` | `object` | |
-| `callback` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any \| void` | |
+| `callback` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any` | |
### Return
diff --git a/docs/collection/merge.md b/docs/collection/merge.md
new file mode 100644
index 0000000..c4aaf88
--- /dev/null
+++ b/docs/collection/merge.md
@@ -0,0 +1,25 @@
+# merge
+
+Merge two objects recursively.
+
+### Usage
+
+```ts
+import { merge } from 'rattail'
+
+merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
+// return { a: 1, b: { c: 2, d: 3 }, e: 4 }
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| -------- | -------- | -------- |
+| `object` | `object` | |
+| `source` | `object` | |
+
+### Return
+
+| Type |
+| -------- |
+| `object` |
diff --git a/docs/function/call.md b/docs/function/call.md
index 26cb090..60a51bc 100644
--- a/docs/function/call.md
+++ b/docs/function/call.md
@@ -7,18 +7,21 @@ Call a single function or multiple functions (passed as an array) and pass argum
```ts
import { call } from 'rattail'
-const fns = [(a, b) => a + b, (a, b) => a * b]
-
-call(fns[0], 1, 2) // return 3
-call(fns, 1, 2) // return [3, 2]
-call(null) // return undefined
+call((a, b) => a + b, 1, 2)
+// return 3
+call([(a, b) => a + b, (a, b) => a + b], 1, 2)
+// return [3, 3]
```
-### Type Declarations
+### Arguments
-```ts
-export function call
(
- fn?: ((...arg: P) => R) | ((...arg: P) => R)[] | null,
- ...args: P
-): R | R[] | undefined
-```
+| Arg | Type | Defaults |
+| --------- | :----------------------: | -------: |
+| `fn` | `Function \| Function[]` | |
+| `...args` | `any[]` | |
+
+### Return
+
+| Type |
+| :--------: |
+| `Function` |
diff --git a/docs/function/debounce.md b/docs/function/debounce.md
index a80a460..cccf762 100644
--- a/docs/function/debounce.md
+++ b/docs/function/debounce.md
@@ -16,13 +16,13 @@ window.addEventListener('resize', debouncedFn)
### Arguments
-| Arg | Type | Defaults |
-| ------- | :-----------------------: | -------: |
-| `fn` | `(...args: any[]) => any` | |
-| `delay` | `number` | 0 |
+| Arg | Type | Defaults |
+| ------- | :--------: | -------: |
+| `fn` | `Function` | |
+| `delay` | `number` | 0 |
### Return
-| Type |
-| :-------------------------------: |
-| `(Parameters) => void` |
+| Type |
+| :--------: |
+| `Function` |
diff --git a/docs/function/throttle.md b/docs/function/throttle.md
index 98d9832..e587b00 100644
--- a/docs/function/throttle.md
+++ b/docs/function/throttle.md
@@ -16,13 +16,13 @@ window.addEventListener('resize', throttledFn)
### Arguments
-| Arg | Type | Defaults |
-| ------- | :-----------------------: | -------: |
-| `fn` | `(...args: any[]) => any` | |
-| `delay` | `number` | 200 |
+| Arg | Type | Defaults |
+| ------- | :--------: | -------: |
+| `fn` | `Function` | |
+| `delay` | `number` | `200` |
### Return
-| Type |
-| :-------------------------------: |
-| `(Parameters) => void` |
+| Type |
+| :--------: |
+| `Function` |
diff --git a/docs/math/max-by.md b/docs/math/max-by.md
new file mode 100644
index 0000000..fff39db
--- /dev/null
+++ b/docs/math/max-by.md
@@ -0,0 +1,25 @@
+# maxBy
+
+Find the maximum value in an array based on the result of applying a function to each element. If the array is empty, `undefined` is returned.
+
+### Usage
+
+```ts
+import { maxBy } from 'rattail'
+
+maxBy([{ n: 5 }, { n: 10 }, { n: 8 }], ({ n }) => n)
+// return { n: 10 }
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| ----- | :------------------: | -------: |
+| `arr` | `T[]` | |
+| `fn` | `(val: T) => number` | |
+
+### Return
+
+| Type |
+| :---------------: |
+| `T \| undefined` |
diff --git a/docs/math/mean-by.md b/docs/math/mean-by.md
new file mode 100644
index 0000000..374b30a
--- /dev/null
+++ b/docs/math/mean-by.md
@@ -0,0 +1,27 @@
+# meanBy
+
+Calculate the mean (average) of an `array` by applying a function to each element to derive a numeric value.
+
+### Usage
+
+```ts
+import { meanBy } from 'rattail'
+
+meanBy([{ n: 4 }, { n: 6 }, { n: 8 }], ({ n }) => n)
+// return 6
+meanBy([10, 20, 30], (n) => n / 2)
+// return 10
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| ----- | :------------------: | -------: |
+| `arr` | `T[]` | |
+| `fn` | `(val: T) => number` | |
+
+### Return
+
+| Type |
+| :------: |
+| `number` |
diff --git a/docs/math/mean.md b/docs/math/mean.md
new file mode 100644
index 0000000..40d3d62
--- /dev/null
+++ b/docs/math/mean.md
@@ -0,0 +1,24 @@
+# mean
+
+Calculate the `mean` (average) of an `array` of `numbers`.
+
+### Usage
+
+```ts
+import { mean } from 'rattail'
+
+mean([1, 2, 3, 4, 5]) // return 3
+mean([10, 20, 30]) // return 20
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| ----- | :--------: | -------: |
+| `arr` | `number[]` | |
+
+### Return
+
+| Type |
+| :------: |
+| `number` |
diff --git a/docs/math/min-by.md b/docs/math/min-by.md
new file mode 100644
index 0000000..f1dbf48
--- /dev/null
+++ b/docs/math/min-by.md
@@ -0,0 +1,25 @@
+# minBy
+
+Find the `minimum` value in an `array` based on the result of applying a function to each element. If the array is empty, `undefined` is returned.
+
+### Usage
+
+```ts
+import { minBy } from 'rattail'
+
+minBy([{ n: 5 }, { n: 2 }, { n: 8 }], ({ n }) => n)
+// return { n: 2 }
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| ----- | :------------------: | -------: |
+| `arr` | `T[]` | |
+| `fn` | `(val: T) => number` | |
+
+### Return
+
+| Type |
+| :---------------: |
+| `T \| undefined` |
diff --git a/docs/math/sample.md b/docs/math/sample.md
new file mode 100644
index 0000000..215eb10
--- /dev/null
+++ b/docs/math/sample.md
@@ -0,0 +1,24 @@
+# sample
+
+Return a random element from an `array`. If the array is empty, `undefined` is returned.
+
+### Usage
+
+```ts
+import { sample } from 'rattail'
+
+sample([1, 2, 3, 4, 5]) // returns a random element, e.g., 3
+sample([]) // returns undefined
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| ----- | :---: | -------: |
+| `arr` | `T[]` | |
+
+### Return
+
+| Type |
+| :---------------: |
+| `T \| undefined` |
diff --git a/docs/array/sum-by.md b/docs/math/sum-by.md
similarity index 85%
rename from docs/array/sum-by.md
rename to docs/math/sum-by.md
index 78cbcf1..f99e42e 100644
--- a/docs/array/sum-by.md
+++ b/docs/math/sum-by.md
@@ -7,7 +7,7 @@ Calculates the sum of values in an `array` based on a provided function.
```ts
import { sumBy } from 'rattail'
-sumBy([{ n: 1 }, { n: 2 }, { n: 3 }], (item) => item.n) // return 6
+sumBy([{ n: 1 }, { n: 2 }, { n: 3 }], ({ n }) => n) // return 6
```
### Arguments
diff --git a/docs/array/sum.md b/docs/math/sum.md
similarity index 100%
rename from docs/array/sum.md
rename to docs/math/sum.md
diff --git a/docs/number/delay.md b/docs/number/delay.md
new file mode 100644
index 0000000..b767f2b
--- /dev/null
+++ b/docs/number/delay.md
@@ -0,0 +1,25 @@
+# delay
+
+Create a promise that resolves after a specified time in milliseconds.
+
+### Usage
+
+```ts
+import { delay } from 'rattail'
+
+console.log('Start')
+await delay(1000)
+console.log('End after 1 second')
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| --------------------- | :------: | -------: |
+| `time (ms)` | `number` | |
+
+### Return
+
+| Type |
+| :-------------: |
+| `Promise` |
diff --git a/docs/number/times.md b/docs/number/times.md
new file mode 100644
index 0000000..868b4f8
--- /dev/null
+++ b/docs/number/times.md
@@ -0,0 +1,27 @@
+# times
+
+Execute a function a specified number of times and return an array of results. Each call to the function is provided with the current index.
+
+### Usage
+
+```ts
+import { times } from 'rattail'
+
+times(3, (index) => index * 2)
+// returns [0, 2, 4]
+times(5, (index) => `Item ${index}`)
+// returns ['Item 0', 'Item 1', 'Item 2', 'Item 3', 'Item 4']
+```
+
+### Arguments
+
+| Arg | Type | Defaults |
+| ----- | :--------------------: | -------: |
+| `num` | `number` | |
+| `fn` | `(index: number) => T` | |
+
+### Return
+
+| Type |
+| :---: |
+| `T[]` |
diff --git a/docs/zh/collection/merge-with.md b/docs/zh/collection/merge-with.md
index 536f1cc..2576262 100644
--- a/docs/zh/collection/merge-with.md
+++ b/docs/zh/collection/merge-with.md
@@ -7,26 +7,17 @@
```ts
import { mergeWith } from 'rattail'
-const object = { a: 1, b: { c: 2 } }
-const source = { b: { d: 3 }, e: 4 }
-const result = mergeWith(object, source)
-// result: { a: 1, b: { c: 2, d: 3 }, e: 4 }
-```
-
-```ts
-const object = { a: [1, 2] }
-const source = { a: [3, 4] }
-const result = mergeWith(object, source, (objValue, srcValue) => [...objValue, ...srcValue])
-// result: { a: [ 1, 2, 3, 4 ] }
+mergeWith({ a: [1, 2] }, { a: [3, 4] }, (objValue, srcValue) => [...objValue, ...srcValue])
+// return { a: [ 1, 2, 3, 4 ] }
```
### 参数
-| 参数 | 类型 | 默认值 |
-| ---------- | ----------------------------------------------------------------------------------------- | ------ |
-| `object` | `object` | |
-| `source` | `object` | |
-| `callback` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any \| void` | |
+| 参数 | 类型 | 默认值 |
+| ---------- | --------------------------------------------------------------------------------- | ------ |
+| `object` | `object` | |
+| `source` | `object` | |
+| `callback` | `(objValue: any, srcValue: any, key: any, object: object, source: object) => any` | |
### 返回值
diff --git a/docs/zh/collection/merge.md b/docs/zh/collection/merge.md
new file mode 100644
index 0000000..594e925
--- /dev/null
+++ b/docs/zh/collection/merge.md
@@ -0,0 +1,25 @@
+# merge
+
+递归合并两个对象。
+
+### 用法
+
+```ts
+import { merge } from 'rattail'
+
+merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
+// return { a: 1, b: { c: 2, d: 3 }, e: 4 }
+```
+
+### 参数
+
+| 参数 | 类型 | 默认值 |
+| -------- | -------- | ------ |
+| `object` | `object` | |
+| `source` | `object` | |
+
+### 返回值
+
+| 类型 |
+| -------- |
+| `object` |
diff --git a/docs/zh/function/call.md b/docs/zh/function/call.md
index 2894266..56bd613 100644
--- a/docs/zh/function/call.md
+++ b/docs/zh/function/call.md
@@ -7,18 +7,21 @@
```ts
import { call } from 'rattail'
-const fns = [(a, b) => a + b, (a, b) => a * b]
-
-call(fns[0], 1, 2) // return 3
-call(fns, 1, 2) // return [3, 2]
-call(null) // return undefined
+call((a, b) => a + b, 1, 2)
+// return 3
+call([(a, b) => a + b, (a, b) => a + b], 1, 2)
+// return [3, 3]
```
-### 类型声明
+### 参数列表
-```ts
-export function call(
- fn?: ((...arg: P) => R) | ((...arg: P) => R)[] | null,
- ...args: P
-): R | R[] | undefined
-```
+| 参数 | 类型 | 默认值 |
+| --------- | :----------------------: | -----: |
+| `fn` | `Function \| Function[]` | |
+| `...args` | `any[]` | |
+
+### 返回值
+
+| 类型 |
+| :--------: |
+| `Function` |
diff --git a/docs/zh/function/debounce.md b/docs/zh/function/debounce.md
index 5b23d99..3a536ec 100644
--- a/docs/zh/function/debounce.md
+++ b/docs/zh/function/debounce.md
@@ -16,13 +16,13 @@ window.addEventListener('resize', debouncedFn)
### 参数列表
-| 参数 | 类型 | 默认值 |
-| ------- | :-----------------------: | -----: |
-| `fn` | `(...args: any[]) => any` | |
-| `delay` | `number` | 0 |
+| 参数 | 类型 | 默认值 |
+| ------- | :--------: | -----: |
+| `fn` | `Function` | |
+| `delay` | `number` | 0 |
### 返回值
-| 类型 |
-| :-------------------------------: |
-| `(Parameters) => void` |
+| 类型 |
+| :--------: |
+| `Function` |
diff --git a/docs/zh/function/throttle.md b/docs/zh/function/throttle.md
index 84faaad..a2387c6 100644
--- a/docs/zh/function/throttle.md
+++ b/docs/zh/function/throttle.md
@@ -16,13 +16,13 @@ window.addEventListener('resize', throttledFn)
### 参数列表
-| 参数 | 类型 | 默认值 |
-| ------- | :-----------------------: | -----: |
-| `fn` | `(...args: any[]) => any` | |
-| `delay` | `number` | 200 |
+| 参数 | 类型 | 默认值 |
+| ------- | :--------: | -----: |
+| `fn` | `Function` | |
+| `delay` | `number` | `200` |
### 返回值
-| 类型 |
-| :-------------------------------: |
-| `(Parameters) => void` |
+| 类型 |
+| :--------: |
+| `Function` |
diff --git a/docs/zh/math/max-by.md b/docs/zh/math/max-by.md
new file mode 100644
index 0000000..7232954
--- /dev/null
+++ b/docs/zh/math/max-by.md
@@ -0,0 +1,25 @@
+# maxBy
+
+在`数组`中查找通过函数应用到每个元素后的`最大值`。如果数组为空,则返回 `undefined`。
+
+### 使用
+
+```ts
+import { maxBy } from 'rattail'
+
+maxBy([{ n: 5 }, { n: 10 }, { n: 8 }], ({ n }) => n)
+// return { n: 10 }
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----- | :------------------: | -----: |
+| `arr` | `T[]` | |
+| `fn` | `(val: T) => number` | |
+
+### 返回值
+
+| 类型 |
+| :--: | ---------- |
+| `T | undefined` |
diff --git a/docs/zh/math/mean-by.md b/docs/zh/math/mean-by.md
new file mode 100644
index 0000000..6727044
--- /dev/null
+++ b/docs/zh/math/mean-by.md
@@ -0,0 +1,27 @@
+# meanBy
+
+通过对`数组`的每个元素应用一个函数以得出数值,计算数组的平均值。
+
+### 使用
+
+```ts
+import { meanBy } from 'rattail'
+
+meanBy([{ n: 4 }, { n: 6 }, { n: 8 }], ({ n }) => n)
+// return 6
+meanBy([10, 20, 30], (n) => n / 2)
+// return 10
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----- | :------------------: | -----: |
+| `arr` | `T[]` | |
+| `fn` | `(val: T) => number` | |
+
+### 返回值
+
+| 类型 |
+| :------: |
+| `number` |
diff --git a/docs/zh/math/mean.md b/docs/zh/math/mean.md
new file mode 100644
index 0000000..93f2e2e
--- /dev/null
+++ b/docs/zh/math/mean.md
@@ -0,0 +1,24 @@
+# mean
+
+计算数字数组的平均值。
+
+### 使用
+
+```ts
+import { mean } from 'rattail'
+
+mean([1, 2, 3, 4, 5]) // return 3
+mean([10, 20, 30]) // return 20
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----- | :---------: | -----: |
+| `arr` | `number[]` | |
+
+### 返回值
+
+| 类型 |
+| :-------: |
+| `number` |
\ No newline at end of file
diff --git a/docs/zh/math/min-by.md b/docs/zh/math/min-by.md
new file mode 100644
index 0000000..954ae32
--- /dev/null
+++ b/docs/zh/math/min-by.md
@@ -0,0 +1,25 @@
+# minBy
+
+在`数组`中查找通过函数应用到每个元素后的`最小值`。如果数组为空,则返回 `undefined`。
+
+### 使用
+
+```ts
+import { minBy } from 'rattail'
+
+minBy([{ n: 5 }, { n: 2 }, { n: 8 }], ({ n }) => n)
+// return { n: 2 }
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----- | :------------------: | -----: |
+| `arr` | `T[]` | |
+| `fn` | `(val: T) => number` | |
+
+### 返回值
+
+| 类型 |
+| :---------------: |
+| `T \| undefined` |
diff --git a/docs/zh/math/sample.md b/docs/zh/math/sample.md
new file mode 100644
index 0000000..d2b2b93
--- /dev/null
+++ b/docs/zh/math/sample.md
@@ -0,0 +1,24 @@
+# sample
+
+从`数组`中返回一个随机元素。如果数组为空,则返回 `undefined`。
+
+### 使用
+
+```ts
+import { sample } from 'rattail'
+
+sample([1, 2, 3, 4, 5]) // return 一个随机元素,例如 3
+sample([]) // return undefined
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----- | :---: | -----: |
+| `arr` | `T[]` | |
+
+### 返回值
+
+| 类型 |
+| :---------------: |
+| `T \| undefined` |
diff --git a/docs/zh/math/sum-by.md b/docs/zh/math/sum-by.md
new file mode 100644
index 0000000..196dad7
--- /dev/null
+++ b/docs/zh/math/sum-by.md
@@ -0,0 +1,24 @@
+# sumBy
+
+根据提供的函数计算`数组`中各项值的和。
+
+### 使用
+
+```ts
+import { sumBy } from 'rattail'
+
+sumBy([{ n: 1 }, { n: 2 }, { n: 3 }], ({ n }) => n) // return 6
+```
+
+### 参数
+
+| 参数 | 类型 | 默认值 |
+| ----- | ---------------------- | ------ |
+| `arr` | `Array` | |
+| `fn` | `(val: any) => number` | |
+
+### 返回值
+
+| 类型 |
+| -------- |
+| `number` |
diff --git a/docs/zh/math/sum.md b/docs/zh/math/sum.md
new file mode 100644
index 0000000..2ddf134
--- /dev/null
+++ b/docs/zh/math/sum.md
@@ -0,0 +1,23 @@
+# sum
+
+计算数字`数组`中各项值的和。
+
+### 使用
+
+```ts
+import { sum } from 'rattail'
+
+sum([1, 2, 3]) // return 6
+```
+
+### 参数
+
+| 参数 | 类型 | 默认值 |
+| ----- | ---------- | ------ |
+| `arr` | `number[]` | |
+
+### 返回值
+
+| 类型 |
+| -------- |
+| `number` |
diff --git a/docs/zh/number/delay.md b/docs/zh/number/delay.md
new file mode 100644
index 0000000..97036f0
--- /dev/null
+++ b/docs/zh/number/delay.md
@@ -0,0 +1,25 @@
+# delay
+
+创建一个在指定的毫秒数后完成的 `promise`。
+
+### 使用
+
+```ts
+import { delay } from 'rattail'
+
+console.log('Start')
+await delay(1000)
+console.log('End after 1 second')
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----------- | :------: | -----: |
+| `time (ms)` | `number` | |
+
+### 返回值
+
+| 类型 |
+| :-------------: |
+| `Promise` |
diff --git a/docs/zh/number/times.md b/docs/zh/number/times.md
new file mode 100644
index 0000000..c385ac0
--- /dev/null
+++ b/docs/zh/number/times.md
@@ -0,0 +1,27 @@
+# times
+
+执行指定次数的函数调用,并返回结果数组。每次调用会传入当前索引。
+
+### 使用
+
+```ts
+import { times } from 'rattail'
+
+times(3, (index) => index * 2)
+// return [0, 2, 4]
+times(5, (index) => `Item ${index}`)
+// return ['Item 0', 'Item 1', 'Item 2', 'Item 3', 'Item 4']
+```
+
+### 参数列表
+
+| 参数 | 类型 | 默认值 |
+| ----- | :--------------------: | -----: |
+| `num` | `number` | |
+| `fn` | `(index: number) => T` | |
+
+### 返回值
+
+| 类型 |
+| :---: |
+| `T[]` |
diff --git a/src/array.ts b/src/array.ts
index 75bad2e..59c006a 100644
--- a/src/array.ts
+++ b/src/array.ts
@@ -80,11 +80,3 @@ export function shuffle(arr: T[]): T[] {
}
return arr
}
-
-export function sum(arr: number[]) {
- return arr.reduce((ret, val) => ret + val, 0)
-}
-
-export function sumBy(arr: T[], fn: (val: T) => number) {
- return arr.reduce((ret, val) => ret + fn(val), 0)
-}
diff --git a/src/collection.ts b/src/collection.ts
index 223574f..699c903 100644
--- a/src/collection.ts
+++ b/src/collection.ts
@@ -3,7 +3,7 @@ import { hasOwn, isArray, isObject } from './general'
export function mergeWith, TSource extends Record>(
object: TObject,
source: TSource,
- callback?: (
+ callback: (
objValue: any,
srcValue: any,
key: string | number | symbol,
@@ -18,7 +18,7 @@ export function mergeWith, TSource extends R
const srcValue = src[key]
const targetValue = target[key]
- const customResult = callback?.(targetValue, srcValue, key, object, source)
+ const customResult = callback(targetValue, srcValue, key, object, source)
if (customResult !== undefined) {
target[key] = customResult
@@ -38,3 +38,10 @@ export function mergeWith, TSource extends R
return baseMerge(object as any, source as any) as TObject & TSource
}
+
+export function merge, TSource extends Record>(
+ object: TObject,
+ source: TSource,
+) {
+ return mergeWith(object, source, () => undefined)
+}
diff --git a/src/index.ts b/src/index.ts
index 13fb6d7..572a848 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,3 +8,4 @@ export * from './collection'
export * from './json'
export * from './file'
export * from './storage'
+export * from './math'
diff --git a/src/math.ts b/src/math.ts
new file mode 100644
index 0000000..ef57d00
--- /dev/null
+++ b/src/math.ts
@@ -0,0 +1,45 @@
+import { randomNumber } from './number'
+
+export function sum(arr: number[]) {
+ return arr.reduce((ret, val) => ret + val, 0)
+}
+
+export function sumBy(arr: T[], fn: (val: T) => number) {
+ return arr.reduce((ret, val) => ret + fn(val), 0)
+}
+
+export function minBy(arr: T[], fn: (val: T) => number) {
+ if (!arr.length) {
+ return
+ }
+
+ return arr.reduce((result, item) => {
+ return fn(result) < fn(item) ? result : item
+ }, arr[0])
+}
+
+export function maxBy(arr: T[], fn: (val: T) => number) {
+ if (!arr.length) {
+ return
+ }
+
+ return arr.reduce((result, item) => {
+ return fn(result) > fn(item) ? result : item
+ }, arr[0])
+}
+
+export function mean(arr: number[]) {
+ return sum(arr) / arr.length
+}
+
+export function meanBy(arr: T[], fn: (val: T) => number) {
+ return sumBy(arr, fn) / arr.length
+}
+
+export function sample(arr: T[]) {
+ if (!arr.length) {
+ return
+ }
+
+ return arr[randomNumber(0, arr.length - 1)]
+}
diff --git a/src/number.ts b/src/number.ts
index 5f2b6ef..fbedcd6 100644
--- a/src/number.ts
+++ b/src/number.ts
@@ -35,3 +35,11 @@ export function genNumberKey() {
export function randomNumber(min = 0, max = 100) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
+
+export function times(num: number, fn: (index: number) => T) {
+ return Array.from({ length: num }, (_, index) => fn(index))
+}
+
+export function delay(time: number) {
+ return new Promise((resolve) => setTimeout(resolve, time))
+}
diff --git a/tests/array.spec.ts b/tests/array.spec.ts
index f96bc74..9456cf4 100644
--- a/tests/array.spec.ts
+++ b/tests/array.spec.ts
@@ -10,8 +10,6 @@ import {
find,
at,
shuffle,
- sum,
- sumBy,
} from '../src'
it('uniq', () => {
@@ -86,11 +84,3 @@ it('shuffle', () => {
const shuffled = shuffle([...arr])
expect(shuffled.sort()).toEqual(arr)
})
-
-it('sum', () => {
- expect(sum([1, 2, 3, 4])).toBe(10)
-})
-
-it('sumBy', () => {
- expect(sumBy([{ value: 1 }, { value: 2 }, { value: 3 }], (item) => item.value)).toBe(6)
-})
diff --git a/tests/collection.spec.ts b/tests/collection.spec.ts
index e86d4b1..03584d5 100644
--- a/tests/collection.spec.ts
+++ b/tests/collection.spec.ts
@@ -1,58 +1,56 @@
-import { describe, it, expect } from 'vitest'
-import { mergeWith } from '../src'
+import { it, expect } from 'vitest'
+import { merge, mergeWith } from '../src'
-describe('mergeWith', () => {
- it('should merge two objects', () => {
- const object = { a: 1, b: { c: 2 } }
- const source = { b: { d: 3 }, e: 4 }
- const result = mergeWith(object, source)
+it('should merge two objects', () => {
+ const object = { a: 1, b: { c: 2 } }
+ const source = { b: { d: 3 }, e: 4 }
+ const result = merge(object, source)
- expect(result).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 })
- })
+ expect(result).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 })
+})
- it('should use the callback to override values', () => {
- const object = { a: 1, b: 2 }
- const source = { a: 3, b: 4 }
- const result = mergeWith(object, source, (objValue, srcValue) => objValue + srcValue)
+it('should handle nested merges', () => {
+ const object = { a: { b: 1 } }
+ const source = { a: { c: 2 } }
+ const result = merge(object, source)
- expect(result).toEqual({ a: 4, b: 6 })
- })
+ expect(result).toEqual({ a: { b: 1, c: 2 } })
- it('should handle nested merges', () => {
- const object = { a: { b: 1 } }
- const source = { a: { c: 2 } }
- const result = mergeWith(object, source)
+ const object2 = { a: 1 }
+ const source2 = { b: { c: 1 } }
+ const result2 = merge(object2, source2)
- expect(result).toEqual({ a: { b: 1, c: 2 } })
+ expect(result2).toEqual({ a: 1, b: { c: 1 } })
+})
- const object2 = { a: 1 }
- const source2 = { b: { c: 1 } }
- const result2 = mergeWith(object2, source2)
+it('should create new properties if they don’t exist in the target', () => {
+ const object = { a: 1 }
+ const source = { b: 2 }
+ const result = merge(object, source)
- expect(result2).toEqual({ a: 1, b: { c: 1 } })
- })
+ expect(result).toEqual({ a: 1, b: 2 })
+})
- it('should create new properties if they don’t exist in the target', () => {
- const object = { a: 1 }
- const source = { b: 2 }
- const result = mergeWith(object, source)
+it('should handle array merging correctly', () => {
+ const object = { a: [1, 2] }
+ const source = { a: [3, 4] }
+ const result = merge(object, source)
- expect(result).toEqual({ a: 1, b: 2 })
- })
+ expect(result).toEqual({ a: [3, 4] })
+})
- it('should handle array merging correctly', () => {
- const object = { a: [1, 2] }
- const source = { a: [3, 4] }
- const result = mergeWith(object, source)
+it('should use callback for array merging', () => {
+ const object = { a: [1, 2] }
+ const source = { a: [3, 4] }
+ const result = mergeWith(object, source, (objValue, srcValue) => [...objValue, ...srcValue])
- expect(result).toEqual({ a: [3, 4] })
- })
+ expect(result).toEqual({ a: [1, 2, 3, 4] })
+})
- it('should use callback for array merging', () => {
- const object = { a: [1, 2] }
- const source = { a: [3, 4] }
- const result = mergeWith(object, source, (objValue, srcValue) => [...objValue, ...srcValue])
+it('should use the callback to override values', () => {
+ const object = { a: 1, b: 2 }
+ const source = { a: 3, b: 4 }
+ const result = mergeWith(object, source, (objValue, srcValue) => objValue + srcValue)
- expect(result).toEqual({ a: [1, 2, 3, 4] })
- })
+ expect(result).toEqual({ a: 4, b: 6 })
})
diff --git a/tests/math.spec.ts b/tests/math.spec.ts
new file mode 100644
index 0000000..caea549
--- /dev/null
+++ b/tests/math.spec.ts
@@ -0,0 +1,34 @@
+import { expect, it } from 'vitest'
+import { sum, sumBy, minBy, maxBy, mean, meanBy, sample } from '../src'
+
+it('sum', () => {
+ expect(sum([1, 2, 3, 4])).toBe(10)
+})
+
+it('sumBy', () => {
+ expect(sumBy([{ value: 1 }, { value: 2 }, { value: 3 }], (item) => item.value)).toBe(6)
+})
+
+it('minBy', () => {
+ expect(minBy([{ value: 1 }, { value: 2 }, { value: 3 }], (item) => item.value)).toEqual({ value: 1 })
+ expect(minBy<{ value: number }>([], (item) => item.value)).toEqual(undefined)
+})
+
+it('maxBy', () => {
+ expect(maxBy([{ value: 1 }, { value: 2 }, { value: 3 }], (item) => item.value)).toEqual({ value: 3 })
+ expect(maxBy<{ value: number }>([], (item) => item.value)).toEqual(undefined)
+})
+
+it('mean', () => {
+ expect(mean([1, 2, 3, 4])).toBe(2.5)
+})
+
+it('meanBy', () => {
+ expect(meanBy([{ value: 1 }, { value: 2 }, { value: 3 }], (item) => item.value)).toBe(2)
+})
+
+it('sample', () => {
+ expect(sample([1, 2, 3, 4])).toBeGreaterThanOrEqual(1)
+ expect(sample([1, 2, 3, 4])).toBeLessThanOrEqual(4)
+ expect(sample([])).toEqual(undefined)
+})
diff --git a/tests/number.spec.ts b/tests/number.spec.ts
index e884a92..d124ee9 100644
--- a/tests/number.spec.ts
+++ b/tests/number.spec.ts
@@ -1,44 +1,55 @@
-import { describe, it, expect } from 'vitest'
-import { toNumber, clamp, clampArrayRange, genNumberKey, randomNumber } from '../src'
+import { it, expect } from 'vitest'
+import { toNumber, clamp, clampArrayRange, genNumberKey, randomNumber, times, delay } from '../src'
-describe('Number utility functions', () => {
- it('should convert various types to number', () => {
- expect(toNumber(null)).toBe(0)
- expect(toNumber(undefined)).toBe(0)
- expect(toNumber('42')).toBe(42)
- expect(toNumber('42.5')).toBe(42.5)
- expect(toNumber('not a number')).toBe(0)
- expect(toNumber(true)).toBe(1)
- expect(toNumber(false)).toBe(0)
- expect(toNumber(100)).toBe(100)
- })
+it('toNumber', () => {
+ expect(toNumber(null)).toBe(0)
+ expect(toNumber(undefined)).toBe(0)
+ expect(toNumber('42')).toBe(42)
+ expect(toNumber('42.5')).toBe(42.5)
+ expect(toNumber('not a number')).toBe(0)
+ expect(toNumber(true)).toBe(1)
+ expect(toNumber(false)).toBe(0)
+ expect(toNumber(100)).toBe(100)
+})
+
+it('clamp', () => {
+ expect(clamp(5, 1, 10)).toBe(5)
+ expect(clamp(0, 1, 10)).toBe(1)
+ expect(clamp(15, 1, 10)).toBe(10)
+})
+
+it('clampArrayRange', () => {
+ const arr = [1, 2, 3, 4, 5]
+ expect(clampArrayRange(2, arr)).toBe(2)
+ expect(clampArrayRange(-1, arr)).toBe(0)
+ expect(clampArrayRange(10, arr)).toBe(4)
+})
- it('should clamp a number within a range', () => {
- expect(clamp(5, 1, 10)).toBe(5)
- expect(clamp(0, 1, 10)).toBe(1)
- expect(clamp(15, 1, 10)).toBe(10)
- })
+it('genNumberKey', () => {
+ const key1 = genNumberKey()
+ const key2 = genNumberKey()
+ expect(key1).not.toBe(key2)
+ expect(key1).toBe(0)
+ expect(key2).toBe(1)
+})
- it('should clamp an index within an array range', () => {
- const arr = [1, 2, 3, 4, 5]
- expect(clampArrayRange(2, arr)).toBe(2)
- expect(clampArrayRange(-1, arr)).toBe(0)
- expect(clampArrayRange(10, arr)).toBe(4)
- })
+it('randomNumber', () => {
+ const min = 5
+ const max = 10
+ const random = randomNumber(min, max)
+ expect(random).toBeGreaterThanOrEqual(min)
+ expect(random).toBeLessThanOrEqual(max)
+ expect(randomNumber(0, 0)).toBe(0)
+})
- it('should generate unique number keys', () => {
- const key1 = genNumberKey()
- const key2 = genNumberKey()
- expect(key1).not.toBe(key2)
- expect(key1).toBe(0)
- expect(key2).toBe(1)
- })
+it('times', () => {
+ const arr = times(5, String)
+ expect(arr).toEqual(['0', '1', '2', '3', '4'])
+})
- it('should generate a random number within a range', () => {
- const min = 5
- const max = 10
- const random = randomNumber(min, max)
- expect(random).toBeGreaterThanOrEqual(min)
- expect(random).toBeLessThanOrEqual(max)
- })
+it('delay', async () => {
+ const start = performance.now()
+ await delay(100)
+ const end = performance.now()
+ expect(end - start).toBeGreaterThanOrEqual(100)
})