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