v_is_empty_value
v2.2.0
Published
Simple checker functions for empty values. Can check if a value is empty or not. [ isEmpty(val) / isNotEmpty(val) ]
Maintainers
Readme
👨💻 v_is_empty_value
Simple checker for Empty/NotEmpty values. Checking Numbers, Null, NaN, Strings, Objects, Arrays, Maps, Sets, Symbols, Functions, BigInt, and more. Will also detect instances of Date, Promise, Error, RegExp, typed arrays, and return "not-empty" value for them.
General Information
It provides 4 core functions to check if a value is empty or not, plus a powerful configuration system.
Core Functions
isEmpty(v, options?): Checks if a value is empty. Returnstrueif empty,falseotherwise.isNotEmpty(v, options?): Checks if a value is not empty. Returnstrueif not empty,falseotherwise.isEmptyNested(v, options?): Checks if a nested value is empty (recursively checks Objects, Arrays, Maps, Sets). Handles circular references automatically.isNotEmptyNested(v, options?): Checks if a nested value is not empty.
Configuration System
Customize behavior globally or per-call:
config.get()- Get current configurationconfig.set(newConfig)- Update global configurationconfig.reset()- Reset to defaultscreateCustomChecker(config)- Create isolated checker with custom config
Configuration Options
| Option | Default | Description |
| :----------------------- | :-------- | :----------------------------------- |
| treatNaNAsEmpty | true | Treat NaN as empty |
| treatFunctionAsEmpty | true | Treat functions as empty |
| treatSymbolAsEmpty | true | Treat symbols as empty |
| treatZeroBigIntAsEmpty | false | Treat 0n as empty |
| checkCircular | true | Enable circular reference detection |
| maxNestedDepth | 0 | Max recursion depth (0 = unlimited) |
Base Example
// import { isEmpty, isNotEmpty, isEmptyNested, isNotEmptyNested } from 'v_is_empty_value'
const { isEmpty, isNotEmpty, isEmptyNested, isNotEmptyNested } = require('v_is_empty_value')
isEmpty(v) // Checks if a value is empty.
isNotEmpty(v) // Checks if a value is not empty.
isEmptyNested(v) // Checks if a nested value is empty.
isNotEmptyNested(v) // Checks if a nested value is not empty.☑ Things it confirms Empty
- Undefined / Empty
console.log(isEmpty()) // prints "true"
console.log(isNotEmpty()) // prints "false"- Empty String
console.log(isEmpty('')) // prints "true"
console.log(isNotEmpty('')) // prints "false"- null
console.log(isEmpty(null)) // prints "true"
console.log(isNotEmpty(null)) // prints "false"- Undefined
console.log(isEmpty(undefined)) // prints "true"
console.log(isNotEmpty(undefined)) // prints "false"- Empty Object
console.log(isEmpty({})) // prints "true"
console.log(isNotEmpty({})) // prints "false"- Empty Array
console.log(isEmpty([])) // prints "true"
console.log(isNotEmpty([])) // prints "false"☑ Few things it confirms NOT Empty
- String with some length/value.
isEmpty('demo_password_123456') // prints "false"
isNotEmpty('demo_password_123456') // prints "true"- Date instance.
isEmpty(new Date()) // prints "false"
isNotEmpty(new Date()) // prints "true"- Error instance.
isEmpty(new Error()) // prints "false"
isNotEmpty(new Error()) // prints "true"- Promise instance.
isEmpty(new Promise((resolve, reject) => resolve(true))) // prints "false"
isNotEmpty(new Promise((resolve, reject) => resolve(true))) // prints "true"- RegExp instance.
isEmpty(/test/) // prints "false"
isEmpty(new RegExp('test')) // prints "false"- Number (including
0and-0).
isEmpty(0) // prints "false"
isEmpty(-0) // prints "false"
isEmpty(42) // prints "false"
isEmpty(Number()) // prints "false" (Number() returns 0)- Boolean (both
trueandfalse).
isEmpty(true) // prints "false"
isEmpty(false) // prints "false"- BigInt (any value including
0nby default).
isEmpty(BigInt(0)) // prints "false"
isEmpty(BigInt(1)) // prints "false"- Map/Set with entries.
isEmpty(new Map([['key', 'value']])) // prints "false"
isEmpty(new Set([1, 2, 3])) // prints "false"- Typed Arrays.
isEmpty(new Uint8Array([1, 2, 3])) // prints "false"- Nested Object : confirms not empty even though it has empty values (surface check only).
const nestedEmptyObject = {
demo: null,
yea: undefined,
iKnowMan: {
wtf: null,
moreNull: null
}
}
// NOTE: isEmpty() returns "false" because the object has keys.
console.log(isEmpty(nestedEmptyObject)) // prints "false"
console.log(isNotEmpty(nestedEmptyObject)) // prints "true"
// NOTE: Use "isEmptyNested(v)" to recursively check nested values.
console.log(isEmptyNested(nestedEmptyObject)) // prints "true"
console.log(isNotEmptyNested(nestedEmptyObject)) // prints "false"📜 More Info:
Check the test cases for more examples.
⚙️ Configuration Examples
Global Configuration
Change behavior globally for all subsequent checks:
import { isEmpty, config } from 'v_is_empty_value'
// By default, NaN is treated as empty
isEmpty(NaN) // prints "true"
// Change global config to treat NaN as non-empty
config.set({ treatNaNAsEmpty: false })
isEmpty(NaN) // prints "false"
// Reset to defaults
config.reset()
isEmpty(NaN) // prints "true" againPer-Call Options
Override configuration for a single call:
import { isEmpty } from 'v_is_empty_value'
// Global default: functions are empty
isEmpty(() => 'test') // prints "true"
// Override for this call only
isEmpty(() => 'test', { treatFunctionAsEmpty: false }) // prints "false"Custom Checker
Create isolated checkers with different configs:
import { createCustomChecker } from 'v_is_empty_value'
const strictChecker = createCustomChecker({
treatNaNAsEmpty: false,
treatFunctionAsEmpty: false,
treatSymbolAsEmpty: false
})
const looseChecker = createCustomChecker({
treatZeroBigIntAsEmpty: true
})
// Strict checker
strictChecker.isEmpty(NaN) // false
strictChecker.isEmpty(() => {}) // false
// Loose checker
looseChecker.isEmpty(BigInt(0)) // true� Lodash Drop-in Replacement
While v_is_empty_value defaults to semantically correct behavior, you can configure it to match Lodash's isEmpty() for drop-in compatibility:
import { isEmpty, config } from 'v_is_empty_value'
// Configure to match Lodash behavior
config.set({
treatNaNAsEmpty: true, // NaN is empty (same default)
treatFunctionAsEmpty: false, // Functions are NOT empty (Lodash style)
treatSymbolAsEmpty: false, // Symbols are NOT empty (Lodash style)
treatZeroBigIntAsEmpty: false, // 0n is NOT empty (same default)
nonEmptyTypes: [] // Disable Date/Promise/Error detection
})
// Now behaves like Lodash:
isEmpty(() => {}) // false (Lodash: false)
isEmpty(Symbol()) // false (Lodash: false)
isEmpty(new Date()) // true (Lodash: true - just checks Object.keys())
isEmpty(new Error()) // true (Lodash: true)
isEmpty(0) // false (Lodash: true ⚠️ STILL DIFFERENT!)
isEmpty(false) // false (Lodash: true ⚠️ STILL DIFFERENT!)Why We Differ From Lodash
We intentionally differ from Lodash on these cases because we believe they are semantically incorrect:
| Value | Lodash | v_is_empty_value | Rationale |
| ------- | -------- | ------------------- | ----------- |
| 0 | empty | non-empty | 0 is a valid numeric value, not "nothing" |
| false | empty | non-empty | false is a valid boolean state, not "no data" |
| new Date() | empty | non-empty | A Date instance represents a timestamp (has value) |
| new Error() | empty | non-empty | An Error represents an error condition (has value) |
| new Promise() | empty | non-empty | A Promise represents async state (has internal slots) |
Lodash uses Object.keys(value).length for objects, which returns 0 for class instances because their data is stored in internal slots, not enumerable properties. We detect these types via constructor.name and treat them as non-empty because they represent values even without enumerable properties.
Note: You cannot make 0 and false return true with configuration - these are hardcoded as non-empty because treating them as empty is considered a design flaw.
�🔍 Special Types Reference
| Type | Empty Behavior | Configurable |
| :------------------------ | :--------------------- | :----------------------- |
| undefined | Empty (true) | No |
| null | Empty (true) | No |
| '' (empty string) | Empty (true) | No |
| NaN | Empty (true) | treatNaNAsEmpty |
| Symbol | Empty (true) | treatSymbolAsEmpty |
| Function | Empty (true) | treatFunctionAsEmpty |
| 0 | Not empty (false) | No |
| BigInt(0) | Not empty (false) | treatZeroBigIntAsEmpty |
| BigInt(n) | Not empty (false) | No |
| true/false | Not empty (false) | No |
| Date | Not empty (false) | No |
| Promise | Not empty (false) | No |
| Error | Not empty (false) | No |
| RegExp | Not empty (false) | No |
| Map (empty) | Empty (true) | No |
| Map (entries) | Not empty (false) | No |
| Set (empty) | Empty (true) | No |
| Set (values) | Not empty (false) | No |
| WeakMap/WeakSet | Not empty (false) | No |
| ArrayBuffer | Not empty (false) | No |
| Typed Arrays | Not empty (false) | No |
| {} (empty object) | Empty (true) | No |
| [] (empty array) | Empty (true) | No |
🔄 Circular Reference Handling
isEmptyNested() automatically handles circular references without infinite loops:
import { isEmptyNested } from 'v_is_empty_value'
const obj = { a: null, b: undefined }
obj.self = obj // Circular reference
// This works without hanging!
isEmptyNested(obj) // prints "true" (all values are empty)
const obj2 = { a: 'value' }
obj2.self = obj2 // Circular reference
isEmptyNested(obj2) // prints "false" (has non-empty value)🚀 Performance Benchmark
This will basically run the functions mentioned for 25mil. times and will print the time taken for each function to complete.
📋 Test setup
- AMD Ryzen 7 2700X Eight-Core Processor 3.70 GHz
- 16 GB 3000 MHz DDR4
- Patriot P300 256GB M.2 NVMe
- Windows 10 Pro 64-bit
- Node.js v20.10.0
📊 Current performance (vs lodash.isEmpty)
| Metric | v_is_empty_value | lodash | Faster | |--------|-----------------|--------|--------| | Throughput | 23,978,672 ops/sec | 3,758,432 ops/sec | 6.4x | | null | 0.89ms | 1.27ms | 1.4x | | empty string | 0.72ms | 1.88ms | 2.6x | | number 0 | 1.63ms | 6.75ms | 4.1x | | NaN | 1.86ms | 5.34ms | 2.9x | | empty Map | 5.98ms | 65.51ms | 11.0x | | Map with entries | 3.36ms | 63.26ms | 18.8x | | empty Set | 7.09ms | 73.90ms | 10.4x | | Uint8Array | 4.01ms | 79.65ms | 19.9x | | Promise | 4.08ms | 68.40ms | 16.7x | | TOTAL | 91.75ms | 585.35ms | 6.4x |
Run npm run benchmark to see full comparison.
🚀 Release Process
For maintainers - steps to publish a new version:
Version Bump
npm version minor # or patch/majorPre-publish Check (runs lint, build, tests)
npm run prepackGit Commit & Tag
git add package.json git commit -m "chore: bump version to X.X.X" git tag vX.X.X git push origin main --tagsNPM Publish
npm publishGitHub Release (Recommended)
Go to GitHub repository → Releases → "Draft a new release"
Click "Choose a tag" and select
vX.X.XRelease title:
vX.X.X - Brief descriptionRelease notes:
## Changes - Feature/fix description - Performance improvements - Bug fixes ## Breaking Changes (if any) - Migration notesAttach build artifacts from
dist/folder (optional)Click "Publish release"
📑 Related links :
- v_to_md5 ⏭ MD5 hash generator
- v_to_sha256 ⏭ sha256 hash generator
- v_file_system ⏭ simple and safe fs module with sync and promises
- v_execute ⏭ Exec cli commands
- v_scrolls ⏭ Readme Generator
- v_database ⏭ single database solution
- v_database_cli ⏭ v_database cli tool
