Skip to content

Commit 6ddde58

Browse files
committed
add mergeWith, transform, words, template docs & examples
1 parent d6fea08 commit 6ddde58

6 files changed

Lines changed: 356 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
## [6.0.0] - 2026-03-30
5+
## [6.0.0] - 2026-04-02
66

77
### ⚠️ BREAKING CHANGES
88

@@ -89,8 +89,8 @@ All notable changes to this project will be documented in this file.
8989
- **50+ New Functions**:
9090
- Array: `chunk`, `flatten`, `groupBy`, `keyBy`, `countBy`, `sample`, `sampleSize`, `intersect`, `intersectionBy`, `union`, `unionBy`, `differenceBy`, `drop`, `dropRight`, `take`, `takeRight`, `findIndex`, `findLastIndex`, `zip`, `unzip`
9191
- Number: `sum`, `average`, `clamp`, `round`, `inRange`
92-
- String: `kebabCase`, `snakeCase`, `truncate`, `capitalize`, `lowerFirst`
93-
- Object: `omit`, `pick`, `isEmpty`, `isNil`, `mapKeys`, `mapValues`, `invert`
92+
- String: `kebabCase`, `snakeCase`, `truncate`, `capitalize`, `lowerFirst`, `words`, `template`
93+
- Object: `omit`, `pick`, `isEmpty`, `isNil`, `mapKeys`, `mapValues`, `invert`, `mergeWith`, `transform`
9494
- Validate: `isEmail`, `isPhone`, `isURL`, `isIDCard`, `isCreditCard`
9595
- Color: `isLightColor`, `hexToRGB`, `rgbToHSL`, `lighten`, `darken`
9696
- Date: `isToday`, `formatDate`, `dateDiff`, `relativeTime`, `getDaysInMonth`

README-zh_CN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ js-cool 提供 **140+ 工具函数**,分为 **16 个类别**:
142142

143143
| 类别 | 描述 | 函数 |
144144
| --------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
145-
| **字符串** | 字符串处理 | `camel2Dash`, `dash2Camel`, `upperFirst`, `lowerFirst`, `capitalize`, `kebabCase`, `snakeCase`, `truncate`, `clearHtml`, `clearAttr`, `cutCHSString`, `getCHSLength`, `mapTemplate`, `escape`, `unescape` |
145+
| **字符串** | 字符串处理 | `camel2Dash`, `dash2Camel`, `upperFirst`, `lowerFirst`, `capitalize`, `kebabCase`, `snakeCase`, `truncate`, `clearHtml`, `clearAttr`, `cutCHSString`, `getCHSLength`, `mapTemplate`, `template`, `words`, `escape`, `unescape` |
146146
| **数组** | 数组处理 | `unique`, `shuffle`, `sorter`, `sortPinyin`, `chunk`, `flatten`, `groupBy`, `keyBy`, `countBy`, `sample`, `sampleSize`, `intersect`, `intersectionBy`, `union`, `unionBy`, `differenceBy`, `minus`, `complement`, `contains`, `all`, `any`, `searchObject`, `drop`, `dropRight`, `take`, `takeRight`, `findIndex`, `findLastIndex`, `zip`, `unzip` |
147-
| **对象** | 对象处理 | `clone`, `extend`, `getProperty`, `setProperty`, `omit`, `pick`, `cleanData`, `safeParse`, `safeStringify`, `mapKeys`, `mapValues`, `invert`, `arrayToCSV`, `CSVToArray` |
147+
| **对象** | 对象处理 | `clone`, `extend`, `getProperty`, `setProperty`, `omit`, `pick`, `cleanData`, `safeParse`, `safeStringify`, `mapKeys`, `mapValues`, `invert`, `mergeWith`, `transform`, `arrayToCSV`, `CSVToArray` |
148148
| **类型判断** | 类型检查 | `getType`, `isArray`, `isObject`, `isPlainObject`, `isDate`, `isRegExp`, `isWindow`, `isIterable`, `isEqual`, `isEmpty`, `isNil` |
149149
| **验证函数** | 验证函数 | `isEmail`, `isPhone`, `isURL`, `isIDCard`, `isCreditCard` |
150150
| **URL与浏览器** | URL解析和浏览器检测 | `getUrlParams`, `getUrlParam`, `parseUrlParam`, `spliceUrlParam`, `getDirParams`, `ua`, `appVersion`, `browserVersion`, `compareVersion`, `nextVersion` |

README.md

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ js-cool provides **140+ utility functions** organized into **16 categories**:
150150

151151
| Category | Description | Functions |
152152
| ----------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
153-
| **String** | String manipulation | `camel2Dash`, `dash2Camel`, `upperFirst`, `lowerFirst`, `capitalize`, `kebabCase`, `snakeCase`, `truncate`, `clearHtml`, `clearAttr`, `cutCHSString`, `getCHSLength`, `mapTemplate`, `escape`, `unescape` |
153+
| **String** | String manipulation | `camel2Dash`, `dash2Camel`, `upperFirst`, `lowerFirst`, `capitalize`, `kebabCase`, `snakeCase`, `truncate`, `clearHtml`, `clearAttr`, `cutCHSString`, `getCHSLength`, `mapTemplate`, `template`, `words`, `escape`, `unescape` |
154154
| **Array** | Array processing | `unique`, `shuffle`, `sorter`, `sortPinyin`, `chunk`, `flatten`, `groupBy`, `keyBy`, `countBy`, `sample`, `sampleSize`, `intersect`, `intersectionBy`, `union`, `unionBy`, `differenceBy`, `minus`, `complement`, `contains`, `all`, `any`, `searchObject`, `drop`, `dropRight`, `take`, `takeRight`, `findIndex`, `findLastIndex`, `zip`, `unzip` |
155-
| **Object** | Object manipulation | `clone`, `extend`, `getProperty`, `setProperty`, `omit`, `pick`, `cleanData`, `safeParse`, `safeStringify`, `mapKeys`, `mapValues`, `invert`, `arrayToCSV`, `CSVToArray` |
155+
| **Object** | Object manipulation | `clone`, `extend`, `getProperty`, `setProperty`, `omit`, `pick`, `cleanData`, `safeParse`, `safeStringify`, `mapKeys`, `mapValues`, `invert`, `mergeWith`, `transform`, `arrayToCSV`, `CSVToArray` |
156156
| **Type Check** | Type checking | `getType`, `isArray`, `isObject`, `isPlainObject`, `isDate`, `isRegExp`, `isWindow`, `isIterable`, `isEqual`, `isEmpty`, `isNil` |
157157
| **Validate** | Validation functions | `isEmail`, `isPhone`, `isURL`, `isIDCard`, `isCreditCard` |
158158
| **URL & Browser** | URL parsing and browser detection | `getUrlParams`, `getUrlParam`, `parseUrlParam`, `spliceUrlParam`, `getDirParams`, `ua`, `appVersion`, `browserVersion`, `compareVersion`, `nextVersion` |
@@ -586,6 +586,64 @@ cutCHSString('abc', 10) // 'abc'
586586
cutCHSString('abc', 10, true) // 'abc'
587587
```
588588

589+
#### words
590+
591+
Split string into an array of words.
592+
593+
```js
594+
import { words } from 'js-cool'
595+
596+
// Default: split by word boundaries
597+
words('fred, barney, & pebbles') // ['fred', 'barney', 'pebbles']
598+
words('camelCaseHTML') // ['camel', 'Case', 'HTML']
599+
words('PascalCase') // ['Pascal', 'Case']
600+
words('snake_case_string') // ['snake', 'case', 'string']
601+
words('kebab-case-string') // ['kebab', 'case', 'string']
602+
603+
// With custom pattern
604+
words('camelCaseHTML', /[A-Z]{2,}/g) // ['HTML']
605+
words('hello world', /\w+/g) // ['hello', 'world']
606+
607+
// Handle numbers
608+
words('version2Update') // ['version', '2', 'Update']
609+
610+
// Consecutive uppercase
611+
words('HTMLParser') // ['HTML', 'Parser']
612+
```
613+
614+
#### template
615+
616+
Simple template engine with variable interpolation.
617+
618+
```js
619+
import { template } from 'js-cool'
620+
621+
// Basic usage
622+
const compiled = template('Hello, {{ name }}!')
623+
compiled({ name: 'World' }) // 'Hello, World!'
624+
625+
// HTML escaping (default)
626+
const safe = template('{{ content }}')
627+
safe({ content: '<script>alert("xss")</script>' })
628+
// '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'
629+
630+
// Raw output (triple braces)
631+
const raw = template('{{{ html }}}')
632+
raw({ html: '<strong>bold</strong>' }) // '<strong>bold</strong>'
633+
634+
// Nested properties
635+
const nested = template('{{ user.name }} is {{ user.age }} years old.')
636+
nested({ user: { name: 'John', age: 30 } }) // 'John is 30 years old.'
637+
638+
// Custom delimiters
639+
const custom = template('Hello, ${ name }!', { open: '${', close: '}' })
640+
custom({ name: 'World' }) // 'Hello, World!'
641+
642+
// Multiple variables
643+
const multi = template('{{ a }} and {{ b }} and {{ c }}')
644+
multi({ a: 1, b: 2, c: 3 }) // '1 and 2 and 3'
645+
```
646+
589647
---
590648

591649
### Array
@@ -984,6 +1042,60 @@ invert({ a: 1, b: 2, c: 1 }) // { '1': 'c', '2': 'b' } (duplicate values: last w
9841042
invert({ x: 'apple', y: 'banana' }) // { apple: 'x', banana: 'y' }
9851043
```
9861044

1045+
#### mergeWith
1046+
1047+
Merge objects with custom strategy function.
1048+
1049+
```js
1050+
import { mergeWith } from 'js-cool'
1051+
1052+
// Custom array merge (concat instead of replace)
1053+
mergeWith({ a: [1, 2] }, { a: [3, 4] }, (objValue, srcValue) => {
1054+
if (Array.isArray(objValue)) {
1055+
return objValue.concat(srcValue)
1056+
}
1057+
})
1058+
// { a: [1, 2, 3, 4] }
1059+
1060+
// Skip certain properties
1061+
mergeWith({ a: 1, b: 2 }, { a: 10, b: 20 }, (objValue, srcValue, key) => {
1062+
if (key === 'b') return objValue // keep original
1063+
})
1064+
// { a: 10, b: 2 }
1065+
1066+
// Merge multiple objects
1067+
mergeWith({ a: 1 }, { b: 2 }, { c: 3 }, (objValue, srcValue) => srcValue ?? objValue)
1068+
// { a: 1, b: 2, c: 3 }
1069+
```
1070+
1071+
#### transform
1072+
1073+
Transform object or array with custom accumulator.
1074+
1075+
```js
1076+
import { transform } from 'js-cool'
1077+
1078+
// Transform object to array
1079+
transform({ a: 1, b: 2, c: 3 }, (result, value, key) => {
1080+
result.push({ key, value })
1081+
return result
1082+
}, [])
1083+
// [{ key: 'a', value: 1 }, { key: 'b', value: 2 }, { key: 'c', value: 3 }]
1084+
1085+
// Filter and transform
1086+
transform({ a: 1, b: 2, c: 3, d: 4 }, (result, value, key) => {
1087+
if (value % 2 === 0) result[key] = value * 2
1088+
}, {})
1089+
// { b: 4, d: 8 }
1090+
1091+
// Early exit by returning false
1092+
transform({ a: 1, b: 2, c: 3 }, (result, value, key) => {
1093+
result[key] = value
1094+
if (key === 'b') return false
1095+
}, {})
1096+
// { a: 1, b: 2 }
1097+
```
1098+
9871099
#### searchObject
9881100
9891101
Deep search in object tree.

examples/src/data/versions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export const versions: Record<string, string> = {
1717
getCHSLength: '1.0.0',
1818
cutCHSString: '1.0.0',
1919
mapTemplate: '2.2.0',
20+
words: '6.0.0',
21+
template: '6.0.0',
2022

2123
// Array
2224
unique: '1.0.0',
@@ -67,6 +69,8 @@ export const versions: Record<string, string> = {
6769
invert: '6.0.0',
6870
mapKeys: '6.0.0',
6971
mapValues: '6.0.0',
72+
mergeWith: '6.0.0',
73+
transform: '6.0.0',
7074

7175
// Typecheck
7276
isArray: '1.0.0',

examples/src/views/Object.vue

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
invert,
1818
mapKeys,
1919
mapValues,
20+
mergeWith,
21+
transform,
2022
} from 'js-cool'
2123
import { useI18n } from '@/locales'
2224
@@ -338,5 +340,115 @@ mapValues({ a: 1, b: 2 }, n => n * 2) // { a: 2, b: 4 }`"
338340
</n-space>
339341
</template>
340342
</FunctionCard>
343+
344+
<!-- mergeWith -->
345+
<FunctionCard
346+
title="mergeWith"
347+
description="Merge objects with custom strategy function"
348+
since="6.0.0"
349+
:code="`// Custom array merge (concat instead of replace)
350+
mergeWith({ a: [1, 2] }, { a: [3, 4] }, (obj, src) => {
351+
if (Array.isArray(obj)) return obj.concat(src)
352+
})
353+
// { a: [1, 2, 3, 4] }
354+
355+
// Skip certain properties
356+
mergeWith({ a: 1, b: 2 }, { a: 10, b: 20 }, (obj, src, key) => {
357+
if (key === 'b') return obj // keep original
358+
})
359+
// { a: 10, b: 2 }`"
360+
>
361+
<template #result>
362+
<n-space vertical>
363+
<n-space align="center">
364+
<code class="code-inline">mergeWith({ a: [1, 2] }, { a: [3, 4] }, concat arrays)</code>
365+
<n-tag type="info" size="small">{{
366+
JSON.stringify(mergeWith({ a: [1, 2] } as any, { a: [3, 4] }, (objValue, srcValue) => {
367+
if (Array.isArray(objValue)) return objValue.concat(srcValue)
368+
}))
369+
}}</n-tag>
370+
</n-space>
371+
<n-space align="center">
372+
<code class="code-inline">mergeWith({ a: 1, b: 2 }, { a: 10, b: 20 }, keep 'b')</code>
373+
<n-tag type="info" size="small">{{
374+
JSON.stringify(mergeWith({ a: 1, b: 2 }, { a: 10, b: 20, c: 30 }, (objValue, srcValue, key) => {
375+
if (key === 'b') return objValue
376+
}))
377+
}}</n-tag>
378+
</n-space>
379+
<n-space align="center">
380+
<code class="code-inline">mergeWith({ a: 1 }, { b: 2 }, { c: 3 }, merge all)</code>
381+
<n-tag type="info" size="small">{{
382+
JSON.stringify(mergeWith({ a: 1 }, { b: 2 }, { c: 3 }, (objValue, srcValue) => srcValue ?? objValue))
383+
}}</n-tag>
384+
</n-space>
385+
</n-space>
386+
</template>
387+
</FunctionCard>
388+
389+
<!-- transform -->
390+
<FunctionCard
391+
title="transform"
392+
description="Transform object to new accumulator with iteratee"
393+
since="6.0.0"
394+
:code="`// Transform object to array
395+
transform({ a: 1, b: 2 }, (result, value, key) => {
396+
result.push({ key, value })
397+
return result
398+
}, [])
399+
// [{ key: 'a', value: 1 }, { key: 'b', value: 2 }]
400+
401+
// Filter and transform
402+
transform({ a: 1, b: 2, c: 3 }, (result, value, key) => {
403+
if (value > 1) result[key] = value * 2
404+
}, {})
405+
// { b: 4, c: 6 }
406+
407+
// Early exit by returning false
408+
transform({ a: 1, b: 2, c: 3 }, (result, value, key) => {
409+
result[key] = value
410+
if (key === 'b') return false
411+
}, {})
412+
// { a: 1, b: 2 }`"
413+
>
414+
<template #result>
415+
<n-space vertical>
416+
<n-space align="center">
417+
<code class="code-inline">transform to array</code>
418+
<n-tag type="info" size="small">{{
419+
JSON.stringify(transform({ a: 1, b: 2, c: 3 }, (result: any[], value, key) => {
420+
result.push({ key, value })
421+
return result
422+
}, []))
423+
}}</n-tag>
424+
</n-space>
425+
<n-space align="center">
426+
<code class="code-inline">filter even & double</code>
427+
<n-tag type="info" size="small">{{
428+
JSON.stringify(transform({ a: 1, b: 2, c: 3, d: 4 }, (result: any, value, key) => {
429+
if (value % 2 === 0) result[key] = value * 2
430+
}, {}))
431+
}}</n-tag>
432+
</n-space>
433+
<n-space align="center">
434+
<code class="code-inline">early exit at 'b'</code>
435+
<n-tag type="info" size="small">{{
436+
JSON.stringify(transform({ a: 1, b: 2, c: 3 }, (result: any, value, key) => {
437+
result[key] = value
438+
if (key === 'b') return false
439+
}, {}))
440+
}}</n-tag>
441+
</n-space>
442+
<n-space align="center">
443+
<code class="code-inline">array to object</code>
444+
<n-tag type="info" size="small">{{
445+
JSON.stringify(transform(['a', 'b', 'c'], (result: any, value, index) => {
446+
result[value] = index
447+
}, {}))
448+
}}</n-tag>
449+
</n-space>
450+
</n-space>
451+
</template>
452+
</FunctionCard>
341453
</div>
342454
</template>

0 commit comments

Comments
 (0)