@helpers4/object 2.0.0-beta.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/lib/index.d.ts +185 -35
- package/lib/index.js +305 -95
- package/lib/index.js.map +1 -1
- package/llms.txt +293 -66
- package/meta/api.json +302 -86
- package/meta/examples.json +82 -17
- package/package.json +8 -4
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @helpers4/object
|
|
2
2
|
|
|
3
3
|
> Tree-shakable TypeScript utility functions for the `object` domain.
|
|
4
|
-
> Package: `@helpers4/object` — Version: 2.0.0
|
|
4
|
+
> Package: `@helpers4/object` — Version: 2.0.0
|
|
5
5
|
> License: LGPL-3.0-or-later
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
@@ -15,7 +15,7 @@ pnpm add @helpers4/object
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import { compact, deepClone,
|
|
18
|
+
import { compact, deepClone, deepMerge, ... } from '@helpers4/object';
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Functions
|
|
@@ -24,15 +24,19 @@ import { compact, deepClone, deepCompare, ... } from '@helpers4/object';
|
|
|
24
24
|
|---|---|
|
|
25
25
|
| `compact` | Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an objec |
|
|
26
26
|
| `deepClone` | Creates a deep copy of an object or array |
|
|
27
|
-
| `deepCompare` | Deep comparison of two objects that returns detailed information about differences. |
|
|
28
27
|
| `deepMerge` | Merges two or more objects deeply |
|
|
28
|
+
| `diff` | Structural object diff. Returns `true` when both inputs are deeply equal, otherwise a DiffResult de |
|
|
29
|
+
| `equalsDeep` | Recursive structural object equality. Boolean wrapper around diff \u2014 returns `true` when the tw |
|
|
30
|
+
| `equalsShallow` | One-level (shallow) object equality. Two objects are equal when they share the exact same set of ow |
|
|
29
31
|
| `get` | Gets a value from an object using a dot-notated path |
|
|
32
|
+
| `groupBy` | Groups an array of items by a key derived from each item. A thin, typed wrapper around `Object.grou |
|
|
33
|
+
| `invert` | Returns a new object with keys and values swapped. If multiple keys share the same value, the last o |
|
|
34
|
+
| `map` | Transforms the values and/or keys of a plain object in a single pass. Both callbacks are optional a |
|
|
30
35
|
| `omit` | Creates a new object without the specified keys. |
|
|
31
36
|
| `pick` | Creates a new object with only the specified keys. |
|
|
32
37
|
| `removeUndefinedNull` | Remove null and undefined values from an object. |
|
|
33
38
|
| `safeJsonParse` | Parses a JSON string, returning `null` (or a fallback) on any parse failure. Unlike `JSON.parse`, t |
|
|
34
39
|
| `set` | Sets a value in an object using a dot-notated path |
|
|
35
|
-
| `shallowEquals` | Quick comparison of two objects using JSON.stringify. This is a fast but simple comparison that may |
|
|
36
40
|
|
|
37
41
|
---
|
|
38
42
|
|
|
@@ -131,22 +135,94 @@ cloned.a.b = 2;
|
|
|
131
135
|
|
|
132
136
|
---
|
|
133
137
|
|
|
134
|
-
### `
|
|
138
|
+
### `deepMerge`
|
|
139
|
+
|
|
140
|
+
Merges two or more objects deeply
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { deepMerge } from '@helpers4/object';
|
|
144
|
+
|
|
145
|
+
deepMerge<T extends Record<string, unknown>>(target: T, sources: Record<string, unknown>[]): T
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Parameters:**
|
|
149
|
+
|
|
150
|
+
- `target: T` — The target object
|
|
151
|
+
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
152
|
+
|
|
153
|
+
**Returns:** `T` — The merged object
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { deepMerge } from '@helpers4/object';
|
|
157
|
+
|
|
158
|
+
deepMerge(target: undefined, sources: Record<string, unknown>[]): undefined
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Parameters:**
|
|
162
|
+
|
|
163
|
+
- `target: undefined` — The target object
|
|
164
|
+
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
165
|
+
|
|
166
|
+
**Returns:** `undefined` — The merged object
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { deepMerge } from '@helpers4/object';
|
|
170
|
+
|
|
171
|
+
deepMerge(target: null, sources: Record<string, unknown>[]): null
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Parameters:**
|
|
175
|
+
|
|
176
|
+
- `target: null` — The target object
|
|
177
|
+
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
178
|
+
|
|
179
|
+
**Returns:** `null` — The merged object
|
|
180
|
+
|
|
181
|
+
**Examples:**
|
|
182
|
+
|
|
183
|
+
*Merge two objects deeply*
|
|
184
|
+
|
|
185
|
+
Recursively merges source properties into the target object.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
|
|
189
|
+
// => { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### `diff`
|
|
195
|
+
|
|
196
|
+
Structural object diff.
|
|
197
|
+
|
|
198
|
+
Returns `true` when both inputs are deeply equal, otherwise a
|
|
199
|
+
DiffResult describing the differences key by key.
|
|
135
200
|
|
|
136
|
-
|
|
201
|
+
Comparison rules:
|
|
202
|
+
- Same reference \u2192 `true`.
|
|
203
|
+
- Either side is `null`/`undefined` (and not both) \u2192 `false`.
|
|
204
|
+
- Both `Date` \u2192 epoch comparison.
|
|
205
|
+
- Both arrays \u2192 compared with `array/equalsDeep` (leaf, no diff drill-down).
|
|
206
|
+
- Special objects (Map, Set, RegExp, Promise, class instances\u2026) \u2192 reference equality.
|
|
207
|
+
- Plain objects \u2192 key-by-key, recursing up to `options.depth` levels.
|
|
208
|
+
- Mixed types (e.g. array vs object, Date vs object) \u2192 `false`.
|
|
209
|
+
|
|
210
|
+
For a boolean wrapper see equalsDeep from this category.
|
|
211
|
+
For a one-level boolean check see equalsShallow from this category.
|
|
137
212
|
|
|
138
213
|
```typescript
|
|
139
|
-
import {
|
|
214
|
+
import { diff } from '@helpers4/object';
|
|
140
215
|
|
|
141
|
-
|
|
216
|
+
diff(objA: object | null | undefined, objB: object | null | undefined, options: DiffOptions): boolean | DiffResult
|
|
142
217
|
```
|
|
143
218
|
|
|
144
219
|
**Parameters:**
|
|
145
220
|
|
|
146
|
-
- `objA: object | null | undefined` — First
|
|
147
|
-
- `objB: object | null | undefined` — Second
|
|
221
|
+
- `objA: object | null | undefined` — First value (object, `null`, or `undefined`).
|
|
222
|
+
- `objB: object | null | undefined` — Second value (object, `null`, or `undefined`).
|
|
223
|
+
- `options: DiffOptions` (default: `{}`) — See DiffOptions.
|
|
148
224
|
|
|
149
|
-
**Returns:** `boolean |
|
|
225
|
+
**Returns:** `boolean | DiffResult` — `true` when equal, otherwise a DiffResult, or `false` for incompatible types.
|
|
150
226
|
|
|
151
227
|
**Examples:**
|
|
152
228
|
|
|
@@ -155,7 +231,7 @@ deepCompare(objA: object | null | undefined, objB: object | null | undefined): b
|
|
|
155
231
|
Deeply compares two objects, returning true when they are structurally equal.
|
|
156
232
|
|
|
157
233
|
```typescript
|
|
158
|
-
|
|
234
|
+
diff({ a: { b: 1 } }, { a: { b: 1 } })
|
|
159
235
|
// => true
|
|
160
236
|
```
|
|
161
237
|
|
|
@@ -164,64 +240,96 @@ deepCompare({ a: { b: 1 } }, { a: { b: 1 } })
|
|
|
164
240
|
Returns a detailed diff object when nested values differ.
|
|
165
241
|
|
|
166
242
|
```typescript
|
|
167
|
-
|
|
243
|
+
diff({ a: { b: 1 } }, { a: { b: 2 } })
|
|
168
244
|
// => { a: { b: false } }
|
|
169
245
|
```
|
|
170
246
|
|
|
171
247
|
---
|
|
172
248
|
|
|
173
|
-
### `
|
|
249
|
+
### `equalsDeep`
|
|
174
250
|
|
|
175
|
-
|
|
251
|
+
Recursive structural object equality.
|
|
252
|
+
|
|
253
|
+
Boolean wrapper around diff \u2014 returns `true` when the two values
|
|
254
|
+
are deeply equal according to the same rules. Use this when you only
|
|
255
|
+
need a yes/no answer; use diff when you also need to know
|
|
256
|
+
*what* differs.
|
|
257
|
+
|
|
258
|
+
For a one-level boolean check use equalsShallow.
|
|
176
259
|
|
|
177
260
|
```typescript
|
|
178
|
-
import {
|
|
261
|
+
import { equalsDeep } from '@helpers4/object';
|
|
179
262
|
|
|
180
|
-
|
|
263
|
+
equalsDeep(objA: object | null | undefined, objB: object | null | undefined): boolean
|
|
181
264
|
```
|
|
182
265
|
|
|
183
266
|
**Parameters:**
|
|
184
267
|
|
|
185
|
-
- `
|
|
186
|
-
- `
|
|
268
|
+
- `objA: object | null | undefined` — First value (object, `null`, or `undefined`).
|
|
269
|
+
- `objB: object | null | undefined` — Second value (object, `null`, or `undefined`).
|
|
187
270
|
|
|
188
|
-
**Returns:** `
|
|
271
|
+
**Returns:** `boolean` — `true` if both inputs are deeply equal, `false` otherwise.
|
|
272
|
+
|
|
273
|
+
**Examples:**
|
|
274
|
+
|
|
275
|
+
*Compare nested objects*
|
|
276
|
+
|
|
277
|
+
Recursive structural equality. Returns true when the two values are deeply equal.
|
|
189
278
|
|
|
190
279
|
```typescript
|
|
191
|
-
|
|
280
|
+
equalsDeep({ a: { b: 1 } }, { a: { b: 1 } })
|
|
281
|
+
// => true
|
|
282
|
+
```
|
|
192
283
|
|
|
193
|
-
|
|
284
|
+
*Detect deep differences*
|
|
285
|
+
|
|
286
|
+
Returns false when nested values differ.
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
equalsDeep({ a: { b: 1 } }, { a: { b: 2 } })
|
|
290
|
+
// => false
|
|
194
291
|
```
|
|
195
292
|
|
|
196
|
-
|
|
293
|
+
---
|
|
197
294
|
|
|
198
|
-
|
|
199
|
-
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
295
|
+
### `equalsShallow`
|
|
200
296
|
|
|
201
|
-
|
|
297
|
+
One-level (shallow) object equality.
|
|
298
|
+
|
|
299
|
+
Two objects are equal when they share the exact same set of own
|
|
300
|
+
enumerable string keys and each pair of values satisfies strict equality
|
|
301
|
+
(`===`). No recursion: nested objects/arrays are compared by reference.
|
|
302
|
+
|
|
303
|
+
Falls back to strict equality when either input is `null`, `undefined`
|
|
304
|
+
or not an object \u2014 so primitives match if and only if they are `===`.
|
|
305
|
+
Arrays are not supported; they always return `false` (unless identical
|
|
306
|
+
references). Use `array/equalsShallow` instead.
|
|
307
|
+
|
|
308
|
+
For recursive structural comparison use equalsDeep. For a diff
|
|
309
|
+
structure use diff.
|
|
202
310
|
|
|
203
311
|
```typescript
|
|
204
|
-
import {
|
|
312
|
+
import { equalsShallow } from '@helpers4/object';
|
|
205
313
|
|
|
206
|
-
|
|
314
|
+
equalsShallow(objA: unknown, objB: unknown): boolean
|
|
207
315
|
```
|
|
208
316
|
|
|
209
317
|
**Parameters:**
|
|
210
318
|
|
|
211
|
-
- `
|
|
212
|
-
- `
|
|
319
|
+
- `objA: unknown` — First value to compare
|
|
320
|
+
- `objB: unknown` — Second value to compare
|
|
213
321
|
|
|
214
|
-
**Returns:** `
|
|
322
|
+
**Returns:** `boolean` — `true` if values are shallowly equal, `false` otherwise.
|
|
215
323
|
|
|
216
324
|
**Examples:**
|
|
217
325
|
|
|
218
|
-
*
|
|
326
|
+
*Compare two equal objects*
|
|
219
327
|
|
|
220
|
-
|
|
328
|
+
Uses JSON.stringify for a fast comparison.
|
|
221
329
|
|
|
222
330
|
```typescript
|
|
223
|
-
|
|
224
|
-
// =>
|
|
331
|
+
equalsShallow({ a: 1, b: 2 }, { a: 1, b: 2 })
|
|
332
|
+
// => true
|
|
225
333
|
```
|
|
226
334
|
|
|
227
335
|
---
|
|
@@ -266,6 +374,156 @@ get({ a: 1 }, 'b.c', 'default')
|
|
|
266
374
|
|
|
267
375
|
---
|
|
268
376
|
|
|
377
|
+
### `groupBy`
|
|
378
|
+
|
|
379
|
+
Groups an array of items by a key derived from each item.
|
|
380
|
+
|
|
381
|
+
A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
|
|
382
|
+
older targets and provides stricter return-type inference.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { groupBy } from '@helpers4/object';
|
|
386
|
+
|
|
387
|
+
groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: function): Partial<Record<K, T[]>>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Parameters:**
|
|
391
|
+
|
|
392
|
+
- `items: readonly T[]` — The array to group
|
|
393
|
+
- `keyFn: function` — Function that returns the group key for each item
|
|
394
|
+
|
|
395
|
+
**Returns:** `Partial<Record<K, T[]>>` — A record mapping each key to the array of items with that key
|
|
396
|
+
|
|
397
|
+
**Examples:**
|
|
398
|
+
|
|
399
|
+
*Group numbers by parity*
|
|
400
|
+
|
|
401
|
+
Groups elements by the string key returned by the callback.
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
groupBy([1, 2, 3, 4], n => n % 2 === 0 ? 'even' : 'odd')
|
|
405
|
+
// => { odd: [1, 3], even: [2, 4] }
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
*Group objects by a property*
|
|
409
|
+
|
|
410
|
+
Use a property accessor as the grouping key.
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
const users = [
|
|
414
|
+
{ name: 'Alice', role: 'admin' },
|
|
415
|
+
{ name: 'Bob', role: 'user' },
|
|
416
|
+
{ name: 'Carol', role: 'admin' },
|
|
417
|
+
];
|
|
418
|
+
groupBy(users, u => u.role)
|
|
419
|
+
// => { admin: [{...Alice}, {...Carol}], user: [{...Bob}] }
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### `invert`
|
|
425
|
+
|
|
426
|
+
Returns a new object with keys and values swapped.
|
|
427
|
+
If multiple keys share the same value, the last one wins.
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
import { invert } from '@helpers4/object';
|
|
431
|
+
|
|
432
|
+
invert<K extends string, V extends PropertyKey>(obj: Record<K, V>): Record<V, K>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
**Parameters:**
|
|
436
|
+
|
|
437
|
+
- `obj: Record<K, V>` — The object whose keys and values are to be swapped
|
|
438
|
+
|
|
439
|
+
**Returns:** `Record<V, K>` — A new object with values as keys and original keys as values
|
|
440
|
+
|
|
441
|
+
**Examples:**
|
|
442
|
+
|
|
443
|
+
*Swap keys and values*
|
|
444
|
+
|
|
445
|
+
Returns a new object with keys and values swapped.
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
invert({ a: 'x', b: 'y', c: 'z' })
|
|
449
|
+
// => { x: 'a', y: 'b', z: 'c' }
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
*Build a reverse lookup map*
|
|
453
|
+
|
|
454
|
+
Useful when you have a code-to-label map and need label-to-code.
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
const STATUS_LABELS = { 200: 'OK', 404: 'Not Found', 500: 'Internal Server Error' };
|
|
458
|
+
const LABEL_TO_CODE = invert(STATUS_LABELS);
|
|
459
|
+
|
|
460
|
+
LABEL_TO_CODE['OK']; // => '200'
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### `map`
|
|
466
|
+
|
|
467
|
+
Transforms the values and/or keys of a plain object in a single pass.
|
|
468
|
+
|
|
469
|
+
Both callbacks are optional and default to identity (no transformation).
|
|
470
|
+
When `mapValue` is omitted the original values are preserved;
|
|
471
|
+
when `mapKey` is omitted the original keys are preserved.
|
|
472
|
+
|
|
473
|
+
Note: if two different keys map to the same output key the last one wins
|
|
474
|
+
(insertion order).
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
import { map } from '@helpers4/object';
|
|
478
|
+
|
|
479
|
+
map<TObj extends Record<string, unknown>, TVal = indexedAccess, TKey extends PropertyKey = keyof TObj>(obj: TObj, mapValue?: function, mapKey?: function): Record<TKey, TVal>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**Parameters:**
|
|
483
|
+
|
|
484
|
+
- `obj: TObj` — The source object
|
|
485
|
+
- `mapValue?: function` — Callback called with `(value, key)` for each entry.
|
|
486
|
+
Defaults to identity.
|
|
487
|
+
- `mapKey?: function` — Callback called with `(key, value)` for each entry.
|
|
488
|
+
Defaults to identity.
|
|
489
|
+
|
|
490
|
+
**Returns:** `Record<TKey, TVal>` — A new object with transformed keys and/or values
|
|
491
|
+
|
|
492
|
+
**Examples:**
|
|
493
|
+
|
|
494
|
+
*Transform values*
|
|
495
|
+
|
|
496
|
+
Maps each value of an object through a transform function.
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
map({ a: 1, b: 2 }, v => v * 10)
|
|
500
|
+
// => { a: 10, b: 20 }
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
*Transform keys*
|
|
504
|
+
|
|
505
|
+
Maps each key of an object through a transform function.
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
map({ a: 1, b: 2 }, undefined, k => k.toUpperCase())
|
|
509
|
+
// => { A: 1, B: 2 }
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
*Transform both keys and values in a single pass*
|
|
513
|
+
|
|
514
|
+
Provide both a value mapper and a key mapper to rewrite the whole object.
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
map(
|
|
518
|
+
{ price: 100, discount: 20 },
|
|
519
|
+
v => v / 100,
|
|
520
|
+
k => `${k}Ratio`
|
|
521
|
+
)
|
|
522
|
+
// => { priceRatio: 1, discountRatio: 0.2 }
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
269
527
|
### `omit`
|
|
270
528
|
|
|
271
529
|
Creates a new object without the specified keys.
|
|
@@ -544,34 +802,3 @@ set({}, 'a.b.c', 42)
|
|
|
544
802
|
```
|
|
545
803
|
|
|
546
804
|
---
|
|
547
|
-
|
|
548
|
-
### `shallowEquals`
|
|
549
|
-
|
|
550
|
-
Quick comparison of two objects using JSON.stringify.
|
|
551
|
-
This is a fast but simple comparison that may not work for all edge cases.
|
|
552
|
-
|
|
553
|
-
```typescript
|
|
554
|
-
import { shallowEquals } from '@helpers4/object';
|
|
555
|
-
|
|
556
|
-
shallowEquals(objA: unknown, objB: unknown): boolean
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
**Parameters:**
|
|
560
|
-
|
|
561
|
-
- `objA: unknown` — First object to compare
|
|
562
|
-
- `objB: unknown` — Second object to compare
|
|
563
|
-
|
|
564
|
-
**Returns:** `boolean` — `true` if objects are identical according to JSON.stringify, `false` otherwise
|
|
565
|
-
|
|
566
|
-
**Examples:**
|
|
567
|
-
|
|
568
|
-
*Compare two equal objects*
|
|
569
|
-
|
|
570
|
-
Uses JSON.stringify for a fast comparison.
|
|
571
|
-
|
|
572
|
-
```typescript
|
|
573
|
-
shallowEquals({ a: 1, b: 2 }, { a: 1, b: 2 })
|
|
574
|
-
// => true
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
---
|