# @helpers4/object

> Tree-shakable TypeScript utility functions for the `object` domain.
> Package: `@helpers4/object` — Version: 2.0.0
> License: LGPL-3.0-or-later

## Installation

```sh
npm install @helpers4/object
# or
pnpm add @helpers4/object
```

## Usage

```typescript
import { compact, deepClone, deepMerge, ... } from '@helpers4/object';
```

## Functions

| Function | Description |
|---|---|
| `compact` | Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an objec |
| `deepClone` | Creates a deep copy of an object or array |
| `deepMerge` | Merges two or more objects deeply |
| `diff` | Structural object diff.  Returns `true` when both inputs are deeply equal, otherwise a DiffResult de |
| `equalsDeep` | Recursive structural object equality.  Boolean wrapper around diff \u2014 returns `true` when the tw |
| `equalsShallow` | One-level (shallow) object equality.  Two objects are equal when they share the exact same set of ow |
| `get` | Gets a value from an object using a dot-notated path |
| `groupBy` | Groups an array of items by a key derived from each item.  A thin, typed wrapper around `Object.grou |
| `invert` | Returns a new object with keys and values swapped. If multiple keys share the same value, the last o |
| `map` | Transforms the values and/or keys of a plain object in a single pass.  Both callbacks are optional a |
| `omit` | Creates a new object without the specified keys. |
| `pick` | Creates a new object with only the specified keys. |
| `removeUndefinedNull` | Remove null and undefined values from an object. |
| `safeJsonParse` | Parses a JSON string, returning `null` (or a fallback) on any parse failure.  Unlike `JSON.parse`, t |
| `set` | Sets a value in an object using a dot-notated path |

---

## API Reference

### `compact`

Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an object.

```typescript
import { compact } from '@helpers4/object';

compact<T extends Record<string, unknown>>(obj: T): Partial<T>
```

**Parameters:**

- `obj: T` — The source object

**Returns:** `Partial<T>` — A new object containing only entries with truthy values

```typescript
import { compact } from '@helpers4/object';

compact(obj: undefined): undefined
```

**Parameters:**

- `obj: undefined` — The source object

**Returns:** `undefined` — A new object containing only entries with truthy values

```typescript
import { compact } from '@helpers4/object';

compact(obj: null): null
```

**Parameters:**

- `obj: null` — The source object

**Returns:** `null` — A new object containing only entries with truthy values

**Examples:**

*Remove falsy values from object*

Removes all entries with falsy values (false, null, undefined, 0, "", NaN).

```typescript
compact({ a: 1, b: null, c: '', d: 0, e: 'hello' })
// => { a: 1, e: 'hello' }
```

*Clean up API response*

Useful to strip empty or missing fields before sending data.

```typescript
compact({ name: 'Alice', email: '', age: 0, role: 'admin' })
// => { name: 'Alice', role: 'admin' }
```

---

### `deepClone`

Creates a deep copy of an object or array

```typescript
import { deepClone } from '@helpers4/object';

deepClone<T>(obj: T): T
```

**Parameters:**

- `obj: T` — The object to clone

**Returns:** `T` — Deep cloned object

**Examples:**

*Clone a nested object*

Creates a deep copy — modifying the clone does not affect the original.

```typescript
const original = { a: { b: 1 } };
const cloned = deepClone(original);
cloned.a.b = 2;
// original.a.b is still 1
```

---

### `deepMerge`

Merges two or more objects deeply

```typescript
import { deepMerge } from '@helpers4/object';

deepMerge<T extends Record<string, unknown>>(target: T, sources: Record<string, unknown>[]): T
```

**Parameters:**

- `target: T` — The target object
- `sources: Record<string, unknown>[]` — The source objects to merge

**Returns:** `T` — The merged object

```typescript
import { deepMerge } from '@helpers4/object';

deepMerge(target: undefined, sources: Record<string, unknown>[]): undefined
```

**Parameters:**

- `target: undefined` — The target object
- `sources: Record<string, unknown>[]` — The source objects to merge

**Returns:** `undefined` — The merged object

```typescript
import { deepMerge } from '@helpers4/object';

deepMerge(target: null, sources: Record<string, unknown>[]): null
```

**Parameters:**

- `target: null` — The target object
- `sources: Record<string, unknown>[]` — The source objects to merge

**Returns:** `null` — The merged object

**Examples:**

*Merge two objects deeply*

Recursively merges source properties into the target object.

```typescript
deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
// => { a: 1, b: { c: 2, d: 3 }, e: 4 }
```

---

### `diff`

Structural object diff.

Returns `true` when both inputs are deeply equal, otherwise a
DiffResult describing the differences key by key.

Comparison rules:
- Same reference \u2192 `true`.
- Either side is `null`/`undefined` (and not both) \u2192 `false`.
- Both `Date` \u2192 epoch comparison.
- Both arrays \u2192 compared with `array/equalsDeep` (leaf, no diff drill-down).
- Special objects (Map, Set, RegExp, Promise, class instances\u2026) \u2192 reference equality.
- Plain objects \u2192 key-by-key, recursing up to `options.depth` levels.
- Mixed types (e.g. array vs object, Date vs object) \u2192 `false`.

For a boolean wrapper see equalsDeep from this category.
For a one-level boolean check see equalsShallow from this category.

```typescript
import { diff } from '@helpers4/object';

diff(objA: object | null | undefined, objB: object | null | undefined, options: DiffOptions): boolean | DiffResult
```

**Parameters:**

- `objA: object | null | undefined` — First value (object, `null`, or `undefined`).
- `objB: object | null | undefined` — Second value (object, `null`, or `undefined`).
- `options: DiffOptions` (default: `{}`) — See DiffOptions.

**Returns:** `boolean | DiffResult` — `true` when equal, otherwise a DiffResult, or `false` for incompatible types.

**Examples:**

*Compare nested objects*

Deeply compares two objects, returning true when they are structurally equal.

```typescript
diff({ a: { b: 1 } }, { a: { b: 1 } })
// => true
```

*Detect deep differences*

Returns a detailed diff object when nested values differ.

```typescript
diff({ a: { b: 1 } }, { a: { b: 2 } })
// => { a: { b: false } }
```

---

### `equalsDeep`

Recursive structural object equality.

Boolean wrapper around diff \u2014 returns `true` when the two values
are deeply equal according to the same rules. Use this when you only
need a yes/no answer; use diff when you also need to know
*what* differs.

For a one-level boolean check use equalsShallow.

```typescript
import { equalsDeep } from '@helpers4/object';

equalsDeep(objA: object | null | undefined, objB: object | null | undefined): boolean
```

**Parameters:**

- `objA: object | null | undefined` — First value (object, `null`, or `undefined`).
- `objB: object | null | undefined` — Second value (object, `null`, or `undefined`).

**Returns:** `boolean` — `true` if both inputs are deeply equal, `false` otherwise.

**Examples:**

*Compare nested objects*

Recursive structural equality. Returns true when the two values are deeply equal.

```typescript
equalsDeep({ a: { b: 1 } }, { a: { b: 1 } })
// => true
```

*Detect deep differences*

Returns false when nested values differ.

```typescript
equalsDeep({ a: { b: 1 } }, { a: { b: 2 } })
// => false
```

---

### `equalsShallow`

One-level (shallow) object equality.

Two objects are equal when they share the exact same set of own
enumerable string keys and each pair of values satisfies strict equality
(`===`). No recursion: nested objects/arrays are compared by reference.

Falls back to strict equality when either input is `null`, `undefined`
or not an object \u2014 so primitives match if and only if they are `===`.
Arrays are not supported; they always return `false` (unless identical
references). Use `array/equalsShallow` instead.

For recursive structural comparison use equalsDeep. For a diff
structure use diff.

```typescript
import { equalsShallow } from '@helpers4/object';

equalsShallow(objA: unknown, objB: unknown): boolean
```

**Parameters:**

- `objA: unknown` — First value to compare
- `objB: unknown` — Second value to compare

**Returns:** `boolean` — `true` if values are shallowly equal, `false` otherwise.

**Examples:**

*Compare two equal objects*

Uses JSON.stringify for a fast comparison.

```typescript
equalsShallow({ a: 1, b: 2 }, { a: 1, b: 2 })
// => true
```

---

### `get`

Gets a value from an object using a dot-notated path

```typescript
import { get } from '@helpers4/object';

get<T = unknown>(obj: unknown, path: string, defaultValue?: T): T | undefined
```

**Parameters:**

- `obj: unknown` — The object to get value from
- `path: string` — The dot-notated path (e.g., 'a.b.c')
- `defaultValue?: T` — Default value if path doesn't exist

**Returns:** `T | undefined` — The value at the path or default value

**Examples:**

*Access a nested property*

Uses a dot-notated path to retrieve a deeply nested value.

```typescript
get({ a: { b: { c: 42 } } }, 'a.b.c')
// => 42
```

*Return default for missing path*

Returns the default value when the path does not exist.

```typescript
get({ a: 1 }, 'b.c', 'default')
// => 'default'
```

---

### `groupBy`

Groups an array of items by a key derived from each item.

A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
older targets and provides stricter return-type inference.

```typescript
import { groupBy } from '@helpers4/object';

groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: function): Partial<Record<K, T[]>>
```

**Parameters:**

- `items: readonly T[]` — The array to group
- `keyFn: function` — Function that returns the group key for each item

**Returns:** `Partial<Record<K, T[]>>` — A record mapping each key to the array of items with that key

**Examples:**

*Group numbers by parity*

Groups elements by the string key returned by the callback.

```typescript
groupBy([1, 2, 3, 4], n => n % 2 === 0 ? 'even' : 'odd')
// => { odd: [1, 3], even: [2, 4] }
```

*Group objects by a property*

Use a property accessor as the grouping key.

```typescript
const users = [
  { name: 'Alice', role: 'admin' },
  { name: 'Bob',   role: 'user'  },
  { name: 'Carol', role: 'admin' },
];
groupBy(users, u => u.role)
// => { admin: [{...Alice}, {...Carol}], user: [{...Bob}] }
```

---

### `invert`

Returns a new object with keys and values swapped.
If multiple keys share the same value, the last one wins.

```typescript
import { invert } from '@helpers4/object';

invert<K extends string, V extends PropertyKey>(obj: Record<K, V>): Record<V, K>
```

**Parameters:**

- `obj: Record<K, V>` — The object whose keys and values are to be swapped

**Returns:** `Record<V, K>` — A new object with values as keys and original keys as values

**Examples:**

*Swap keys and values*

Returns a new object with keys and values swapped.

```typescript
invert({ a: 'x', b: 'y', c: 'z' })
// => { x: 'a', y: 'b', z: 'c' }
```

*Build a reverse lookup map*

Useful when you have a code-to-label map and need label-to-code.

```typescript
const STATUS_LABELS = { 200: 'OK', 404: 'Not Found', 500: 'Internal Server Error' };
const LABEL_TO_CODE = invert(STATUS_LABELS);

LABEL_TO_CODE['OK']; // => '200'
```

---

### `map`

Transforms the values and/or keys of a plain object in a single pass.

Both callbacks are optional and default to identity (no transformation).
When `mapValue` is omitted the original values are preserved;
when `mapKey` is omitted the original keys are preserved.

Note: if two different keys map to the same output key the last one wins
(insertion order).

```typescript
import { map } from '@helpers4/object';

map<TObj extends Record<string, unknown>, TVal = indexedAccess, TKey extends PropertyKey = keyof TObj>(obj: TObj, mapValue?: function, mapKey?: function): Record<TKey, TVal>
```

**Parameters:**

- `obj: TObj` — The source object
- `mapValue?: function` — Callback called with `(value, key)` for each entry.
  Defaults to identity.
- `mapKey?: function` — Callback called with `(key, value)` for each entry.
  Defaults to identity.

**Returns:** `Record<TKey, TVal>` — A new object with transformed keys and/or values

**Examples:**

*Transform values*

Maps each value of an object through a transform function.

```typescript
map({ a: 1, b: 2 }, v => v * 10)
// => { a: 10, b: 20 }
```

*Transform keys*

Maps each key of an object through a transform function.

```typescript
map({ a: 1, b: 2 }, undefined, k => k.toUpperCase())
// => { A: 1, B: 2 }
```

*Transform both keys and values in a single pass*

Provide both a value mapper and a key mapper to rewrite the whole object.

```typescript
map(
  { price: 100, discount: 20 },
  v => v / 100,
  k => `${k}Ratio`
)
// => { priceRatio: 1, discountRatio: 0.2 }
```

---

### `omit`

Creates a new object without the specified keys.

```typescript
import { omit } from '@helpers4/object';

omit<T extends Record<string, unknown>, K extends string | number | symbol>(obj: T, keys: readonly K[]): Omit<T, K>
```

**Parameters:**

- `obj: T` — The source object
- `keys: readonly K[]` — The keys to omit

**Returns:** `Omit<T, K>` — A new object without the omitted keys

```typescript
import { omit } from '@helpers4/object';

omit(obj: undefined, keys: readonly string[]): undefined
```

**Parameters:**

- `obj: undefined` — The source object
- `keys: readonly string[]` — The keys to omit

**Returns:** `undefined` — A new object without the omitted keys

```typescript
import { omit } from '@helpers4/object';

omit(obj: null, keys: readonly string[]): null
```

**Parameters:**

- `obj: null` — The source object
- `keys: readonly string[]` — The keys to omit

**Returns:** `null` — A new object without the omitted keys

**Examples:**

*Omit specific keys*

Creates a new object without the specified keys.

```typescript
omit({ a: 1, b: 2, c: 3 }, ['b'])
// => { a: 1, c: 3 }
```

*Remove sensitive fields*

Useful to strip sensitive data before sending to client.

```typescript
const user = { id: 1, name: 'Alice', password: 'secret', token: 'abc123' };
omit(user, ['password', 'token'])
// => { id: 1, name: 'Alice' }
```

---

### `pick`

Creates a new object with only the specified keys.

```typescript
import { pick } from '@helpers4/object';

pick<T extends Record<string, unknown>, K extends string | number | symbol>(obj: T, keys: readonly K[]): Pick<T, K>
```

**Parameters:**

- `obj: T` — The source object
- `keys: readonly K[]` — The keys to pick

**Returns:** `Pick<T, K>` — A new object with only the picked keys

```typescript
import { pick } from '@helpers4/object';

pick(obj: undefined, keys: readonly string[]): undefined
```

**Parameters:**

- `obj: undefined` — The source object
- `keys: readonly string[]` — The keys to pick

**Returns:** `undefined` — A new object with only the picked keys

```typescript
import { pick } from '@helpers4/object';

pick(obj: null, keys: readonly string[]): null
```

**Parameters:**

- `obj: null` — The source object
- `keys: readonly string[]` — The keys to pick

**Returns:** `null` — A new object with only the picked keys

**Examples:**

*Pick specific keys*

Creates a new object with only the specified keys.

```typescript
pick({ a: 1, b: 2, c: 3 }, ['a', 'c'])
// => { a: 1, c: 3 }
```

*Extract user fields*

Useful to select only the fields you need from an object.

```typescript
const user = { id: 1, name: 'Alice', email: 'alice@example.com', password: 'secret' };
pick(user, ['id', 'name', 'email'])
// => { id: 1, name: 'Alice', email: 'alice@example.com' }
```

---

### `removeUndefinedNull`

Remove null and undefined values from an object.

```typescript
import { removeUndefinedNull } from '@helpers4/object';

removeUndefinedNull<T extends Record<string, string | number | boolean | null | undefined>>(obj: T): Partial<T>
```

**Parameters:**

- `obj: T` — an object

**Returns:** `Partial<T>` — A shallow copy of the object without null or undefined values

```typescript
import { removeUndefinedNull } from '@helpers4/object';

removeUndefinedNull(obj: null): null
```

**Parameters:**

- `obj: null` — a null object

**Returns:** `null` — null

```typescript
import { removeUndefinedNull } from '@helpers4/object';

removeUndefinedNull(obj: undefined): undefined
```

**Parameters:**

- `obj: undefined` — an undefined object

**Returns:** `undefined` — undefined

**Examples:**

*Strip null and undefined values*

Returns a shallow copy of the object without null or undefined properties.

```typescript
removeUndefinedNull({ a: 1, b: null, c: undefined, d: 'ok' })
// => { a: 1, d: 'ok' }
```

---

### `safeJsonParse`

Parses a JSON string, returning `null` (or a fallback) on any parse failure.

Unlike `JSON.parse`, this never throws. Invalid JSON strings and other
parsing edge-cases resolve to `null` or the provided `fallback`.

```typescript
import { safeJsonParse } from '@helpers4/object';

safeJsonParse<T>(input: string): T | null
```

**Parameters:**

- `input: string` — The JSON string to parse.

**Returns:** `T | null` — The parsed value typed as `T`, or `fallback` on failure.

```typescript
import { safeJsonParse } from '@helpers4/object';

safeJsonParse<T>(input: string, fallback: T): T
```

**Parameters:**

- `input: string` — The JSON string to parse.
- `fallback: T` — Value returned on failure. Defaults to `null` when omitted.

**Returns:** `T` — The parsed value typed as `T`, or `fallback` on failure.

**Examples:**

*Parse valid JSON*

Returns the parsed value when the input is valid JSON.

```typescript
safeJsonParse<{ a: number }>('{"a":1}')
// => { a: 1 }
```

*Return null on invalid input*

Returns null instead of throwing when JSON is malformed.

```typescript
safeJsonParse('invalid')
// => null
```

*Use a fallback value*

Returns the provided fallback when parsing fails.

```typescript
safeJsonParse('invalid', [])
// => []
```

---

### `set`

Sets a value in an object using a dot-notated path

```typescript
import { set } from '@helpers4/object';

set(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown>
```

**Parameters:**

- `obj: Record<string, unknown>` — The object to set value in
- `path: string` — The dot-notated path (e.g., 'a.b.c')
- `value: unknown` — The value to set

**Returns:** `Record<string, unknown>` — The modified object

**Examples:**

*Set a nested property*

Creates intermediate objects as needed along the dot-notated path.

```typescript
set({}, 'a.b.c', 42)
// => { a: { b: { c: 42 } } }
```

---
