es-toolkit-clean
Version:
A lightweight, TypeScript-first library for cleaning JavaScript objects by removing undefined values, empty strings, empty arrays, functions, and other unwanted properties. Built on es-toolkit for superior performance.
500 lines (400 loc) โข 13.6 kB
Markdown
# es-toolkit-clean
A lightweight, TypeScript-first library for cleaning JavaScript objects by removing undefined values, empty strings, empty arrays, functions, and other unwanted properties. Built on [es-toolkit](https://github.com/toss/es-toolkit) for superior performance.
## Why es-toolkit-clean?
Sometimes API responses contain properties filled with empty strings, undefined values, or functions that you want to remove. This library provides a configurable way to clean your data structures, similar to a more powerful version of `_.compact` for objects.
## Features
- ๐งน **Smart cleaning** - Removes empty strings, undefined values, empty arrays, and functions by default
- ๐ง **Fully configurable** - Customize how each data type should be processed
- ๐ฆ **Lightweight** - Small bundle size (ESM: ~4.21KB, CJS: ~7.37KB) uncompressed.
- ๐ฑ **Universal** - Works in Node.js, browsers, and edge environments
- ๐ **TypeScript-first** - Full TypeScript support with comprehensive type definitions
- โก **High performance** - Built on es-toolkit for optimal speed
- ๐ฏ **Dual format** - Supports both ESM and CommonJS
## Installation
```bash
npm install es-toolkit-clean
```
```bash
pnpm add es-toolkit-clean
```
```bash
yarn add es-toolkit-clean
```
## Quick Start
```javascript
import clean from 'es-toolkit-clean'
const messy = {
name: 'John',
email: '', // Empty string - will be removed
age: 0, // Zero - will be kept
active: true, // Boolean - will be kept
score: null, // Null - will be kept
undefined: undefined, // Undefined - will be removed
fn: () => {}, // Function - will be removed
tags: ['', 'valid', ' '], // Mixed array - empty strings removed
nested: {
value: 'keep',
empty: '',
spaces: ' '
}
}
const cleaned = clean(messy)
console.log(cleaned)
// Output:
// {
// name: 'John',
// age: 0,
// active: true,
// score: null,
// tags: ['valid'],
// nested: { value: 'keep' }
// }
```
## Core API
### `clean(obj)` - Default Export
The main function for cleaning any data structure with default settings.
```javascript
import clean from 'es-toolkit-clean'
// Clean an object
const result = clean({ name: 'test', empty: '', valid: 42 })
// โ { name: 'test', valid: 42 }
// Clean an array
const cleanArray = clean(['valid', '', ' ', 'another'])
// โ ['valid', 'another']
// Clean a string
const cleanString = clean(' hello world ')
// โ 'hello world'
```
### `createCleaner(config)` - Custom Cleaning
Create a custom cleaner with specific configuration for different data types.
```javascript
import { createCleaner, defaultProcessors } from 'es-toolkit-clean'
// Remove null values (default behavior keeps them)
const strictCleaner = createCleaner({
...defaultProcessors,
isNull: () => undefined // Remove nulls instead of keeping them
})
const result = strictCleaner({
name: 'test',
value: null,
empty: ''
})
// โ { name: 'test' }
// Keep functions (default behavior removes them)
const keepFunctions = createCleaner({
...defaultProcessors,
isFunction: (fn) => fn // Keep functions instead of removing them
})
const withFn = keepFunctions({
name: 'test',
callback: () => 'hello',
empty: ''
})
// โ { name: 'test', callback: [Function] }
```
### `createProcessor(config)` - Custom Value Processing
Create a custom processor for handling individual values.
```javascript
import { createProcessor, defaultProcessors } from 'es-toolkit-clean'
// Custom string processing
const customProcessor = createProcessor({
...defaultProcessors,
isString: (str) => {
const trimmed = str.trim()
return trimmed ? trimmed.toUpperCase() : undefined
}
})
const processor = customProcessor
const result = processor(' hello world ', processor)
// โ 'HELLO WORLD'
```
### `processValue(value, processor)` - Process Single Values
Process individual values using the default or a custom processor.
```javascript
import { processValue, createProcessor } from 'es-toolkit-clean'
// Using default processor
const cleaned = processValue(' hello ', processValue)
// โ 'hello'
// Using custom processor
const upperProcessor = createProcessor({
isString: (str) => str.trim().toUpperCase() || undefined
})
const result = processValue(' hello ', upperProcessor)
// โ 'HELLO'
```
## Configuration Options
The `defaultProcessors` object defines how each data type is handled:
```javascript
import { defaultProcessors } from 'es-toolkit-clean'
// Default configuration:
const config = {
isArray: cleanArray, // Recursively clean arrays, remove if empty
isBoolean: identity, // Keep all boolean values
isDate: identity, // Keep all date values
isFunction: noop, // Remove all functions
isNull: identity, // Keep all null values
isNumber: identity, // Keep all number values (including 0)
isObjectLike: cleanObject, // Clean class instances, convert to plain objects
isPlainObject: cleanObject, // Recursively clean objects, remove if empty
isString: cleanString, // Trim whitespace, remove if empty
isUndefined: noop, // Remove all undefined values
}
```
### Customization Examples
```javascript
import { createCleaner, defaultProcessors } from 'es-toolkit-clean'
// Remove all falsy values except 0
const removeFalsy = createCleaner({
...defaultProcessors,
isNull: () => undefined,
isBoolean: (val) => val || undefined,
isString: (str) => str.trim() || undefined
})
// Custom number processing
const positiveNumbers = createCleaner({
...defaultProcessors,
isNumber: (num) => num > 0 ? num : undefined
})
// Custom array processing
const nonEmptyArrays = createCleaner({
...defaultProcessors,
isArray: (arr, clean) => {
const cleaned = arr.map(item => clean(item, clean))
.filter(item => item !== undefined)
return cleaned.length >= 2 ? cleaned : undefined // Require at least 2 items
}
})
```
## Advanced Examples
### Complex Object Cleaning
```javascript
import clean from 'es-toolkit-clean'
const apiResponse = {
user: {
id: 123,
name: 'John Doe',
email: '',
phone: ' ',
profile: {
bio: '',
avatar: 'https://example.com/avatar.jpg',
settings: {
theme: '',
notifications: true,
metadata: {}
}
}
},
posts: [
{ title: 'Hello', content: '', tags: ['', 'intro'] },
{ title: '', content: '', tags: [] },
{ title: 'World', content: 'Content here', tags: ['update', ''] }
],
emptyArray: [],
nullValue: null,
undefinedValue: undefined
}
const cleaned = clean(apiResponse)
console.log(cleaned)
// Output:
// {
// user: {
// id: 123,
// name: 'John Doe',
// profile: {
// avatar: 'https://example.com/avatar.jpg',
// settings: {
// notifications: true
// }
// }
// },
// posts: [
// { title: 'Hello', tags: ['intro'] },
// { title: 'World', content: 'Content here', tags: ['update'] }
// ],
// nullValue: null
// }
```
### Working with Different Data Types
```javascript
import clean from 'es-toolkit-clean'
// Array cleaning
const messyArray = ['valid', '', ' ', null, undefined, 0, false, 'another']
const cleanedArray = clean(messyArray)
// โ ['valid', null, 0, false, 'another']
// String cleaning
const messyString = ' hello world '
const cleanedString = clean(messyString)
// โ 'hello world'
// Nested structures
const nested = {
level1: {
level2: {
level3: {
value: '',
keep: 'this'
},
empty: ''
},
alsoEmpty: []
}
}
const cleanedNested = clean(nested)
// โ { level1: { level2: { level3: { keep: 'this' } } } }
```
### Class Instance Cleaning
The library automatically handles class instances by converting them to plain objects while cleaning their properties:
```javascript
import clean, { createCleaner } from 'es-toolkit-clean'
class User {
constructor(data) {
this.name = data.name
this.email = data.email
this.role = data.role
}
}
class ApiResponse {
constructor(data) {
this.user = new User(data.user)
this.metadata = data.metadata
this.timestamp = new Date()
}
}
const response = new ApiResponse({
user: {
name: 'John',
email: '', // Empty string - will be removed
role: 'admin'
},
metadata: {
version: '', // Empty string - will be removed
source: 'api'
}
})
const cleaned = clean(response)
console.log(cleaned)
// Output: Plain object (not ApiResponse instance)
// {
// user: { name: 'John', role: 'admin' },
// metadata: { source: 'api' },
// timestamp: 2025-06-10T12:00:00.000Z
// }
// Class instances become plain objects
console.log(cleaned.constructor.name) // โ 'Object'
console.log(response.constructor.name) // โ 'ApiResponse'
```
The library intelligently distinguishes between:
- **Class instances** (converted to plain objects and cleaned)
- **Plain objects** (cleaned in place)
- **Special objects** (Date, RegExp, Map, Set, Error - preserved as-is)
```javascript
import clean from 'es-toolkit-clean'
const mixed = {
classInstance: new User({ name: 'test', email: '' }),
plainObject: { value: 'keep', empty: '' },
date: new Date(),
regex: /pattern/g,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3])
}
const result = clean(mixed)
// โ {
// classInstance: { name: 'test' }, // Class โ plain object, cleaned
// plainObject: { value: 'keep' }, // Plain object, cleaned
// date: 2025-06-10T12:00:00.000Z, // Date preserved
// regex: /pattern/g, // RegExp preserved
// map: Map(1) { 'key' => 'value' }, // Map preserved
// set: Set(3) { 1, 2, 3 } // Set preserved
// }
```
## Helper Functions
The library also exports individual helper functions for specific use cases:
```javascript
import { cleanArray, cleanObject, cleanString } from 'es-toolkit-clean'
// Clean individual strings
const cleaned = cleanString(' hello ') // โ 'hello'
const empty = cleanString(' ') // โ undefined
// Clean arrays with custom processor
import { createProcessor } from 'es-toolkit-clean'
const processor = createProcessor()
const cleanedArray = cleanArray(['test', '', 'valid'], processor)
// โ ['test', 'valid']
// Clean objects with custom processor
const cleanedObject = cleanObject({
name: 'test',
empty: '',
value: 42
}, processor)
// โ { name: 'test', value: 42 }
```
## TypeScript Support
The library is written in TypeScript and provides comprehensive type definitions:
```typescript
import clean, {
createCleaner,
createProcessor,
defaultProcessors,
type ProcessorConfig,
type CleanerFunction
} from 'es-toolkit-clean'
// Custom processor with full type safety
const customCleaner: CleanerFunction = createCleaner({
...defaultProcessors,
isString: (str: string): string | undefined => {
return str.length > 5 ? str.toLowerCase() : undefined
}
})
interface User {
name: string
email?: string
age: number
}
const user: User = { name: 'John', email: '', age: 25 }
const cleaned = clean(user) // Fully typed
```
## Performance
Built on es-toolkit, this library offers excellent performance characteristics:
- **Tree-shakable**: Only include the functions you use
- **Zero dependencies**: Apart from es-toolkit core functions
- **Optimized algorithms**: Efficient recursive processing
- **Memory efficient**: Creates new objects without mutating originals
## Development
```bash
# Install dependencies
pnpm install
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Build the library
pnpm build
# Type checking
pnpm typecheck
# Linting
pnpm lint
```
## API Reference
### Functions
| Function | Description | Parameters | Returns |
|----------|-------------|------------|---------|
| `clean(obj)` | Clean any data structure with default settings | `obj: unknown` | `unknown` |
| `createCleaner(config)` | Create custom cleaner function | `config: Partial<ProcessorConfig>` | `(obj: unknown) => unknown` |
| `createProcessor(config)` | Create custom value processor | `config: Partial<ProcessorConfig>` | `CleanerFunction` |
| `processValue(value, processor)` | Process single value | `value: unknown, processor: CleanerFunction` | `unknown` |
### Configuration
| Property | Type | Default Behavior | Customizable |
|----------|------|------------------|--------------|
| `isArray` | `(arr: unknown[], clean: CleanerFunction) => unknown` | Recursively clean, remove if empty | โ
|
| `isBoolean` | `(val: boolean) => unknown` | Keep all booleans | โ
|
| `isDate` | `(date: Date) => unknown` | Keep all dates | โ
|
| `isFunction` | `(fn: Function) => unknown` | Remove all functions | โ
|
| `isNull` | `(val: null) => unknown` | Keep all nulls | โ
|
| `isNumber` | `(num: number) => unknown` | Keep all numbers | โ
|
| `isObjectLike` | `(obj: Record<string, unknown>, clean: CleanerFunction) => unknown` | Clean class instances, convert to plain objects | โ
|
| `isPlainObject` | `(obj: Record<string, unknown>, clean: CleanerFunction) => unknown` | Recursively clean, remove if empty | โ
|
| `isString` | `(str: string) => unknown` | Trim, remove if empty | โ
|
| `isUndefined` | `(val: undefined) => unknown` | Remove all undefined | โ
|
## License
MIT ยฉ [Serhii Siryk](https://github.com/sqrter)
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.