{"id":30246800,"url":"https://github.com/glideapps/loose-deep-equal","last_synced_at":"2025-08-15T05:40:31.508Z","repository":{"id":308263167,"uuid":"1032190872","full_name":"glideapps/loose-deep-equal","owner":"glideapps","description":"JavaScript fast deep equality, but treating missing/undefined properties as equal","archived":false,"fork":false,"pushed_at":"2025-08-05T01:31:16.000Z","size":56,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-05T02:37:30.863Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/glideapps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-05T00:38:32.000Z","updated_at":"2025-08-05T01:29:59.000Z","dependencies_parsed_at":"2025-08-05T02:37:32.791Z","dependency_job_id":"7cca02d9-ed51-4112-91ef-f56d9ddcbee6","html_url":"https://github.com/glideapps/loose-deep-equal","commit_stats":null,"previous_names":["glideapps/loose-deep-equal"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/glideapps/loose-deep-equal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glideapps%2Floose-deep-equal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glideapps%2Floose-deep-equal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glideapps%2Floose-deep-equal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glideapps%2Floose-deep-equal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/glideapps","download_url":"https://codeload.github.com/glideapps/loose-deep-equal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glideapps%2Floose-deep-equal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270528936,"owners_count":24601126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-15T02:00:12.559Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-15T05:40:29.341Z","updated_at":"2025-08-15T05:40:31.477Z","avatar_url":"https://github.com/glideapps.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# loose-deep-equal\n\n[![Tests](https://github.com/glideapps/loose-deep-equal/actions/workflows/test.yml/badge.svg)](https://github.com/glideapps/loose-deep-equal/actions/workflows/test.yml)\n[![npm version](https://badge.fury.io/js/loose-deep-equal.svg)](https://www.npmjs.com/package/loose-deep-equal)\n\nFast deep equality comparison that treats missing properties as equal to `undefined`.\n\n## Why?\n\nStandard deep equality functions treat these objects as different:\n```javascript\nconst obj1 = { a: 1 };\nconst obj2 = { a: 1, b: undefined };\n```\n\nBut in many real-world scenarios (API responses, optional fields, configuration objects), you want these to be considered equal. That's what `loose-deep-equal` does.\n\n## Features\n\n- 🚀 **Fast** - Optimized for performance with early exits and minimal overhead\n- 🎯 **Loose equality** - Missing properties are treated as `undefined`\n- 📦 **Zero dependencies** - Small and self-contained\n- 🔧 **Drop-in replacement** - Compatible with other deep equality functions\n- 🆕 **ES6+ Support** - Full support for Maps, Sets, TypedArrays, and BigInt\n\n## Installation\n\n```bash\nnpm install loose-deep-equal\n```\n\n## Usage\n\n### CommonJS\n```javascript\nconst looseEqual = require('loose-deep-equal');\n\nlooseEqual({ a: 1 }, { a: 1, b: undefined }); // true\n```\n\n### ES Modules\n```javascript\n// Default import\nimport looseEqual from 'loose-deep-equal';\n\n// Named import\nimport { looseEqual } from 'loose-deep-equal';\n\nlooseEqual({ a: 1 }, { a: 1, b: undefined }); // true\n```\n\n### Examples\n\n```javascript\n// Missing properties are treated as undefined\nlooseEqual({ a: 1 }, { a: 1, b: undefined }); // true\n\n// Regular deep equality still works\nlooseEqual({ a: { b: 2 } }, { a: { b: 2 } }); // true\n\n// null is not undefined\nlooseEqual({ a: null }, { a: undefined }); // false\nlooseEqual({ a: null }, {}); // false\n\n// Works with arrays\nlooseEqual([1, 2], [1, 2]); // true\n\n// Works with nested structures\nlooseEqual(\n  { user: { name: 'John' } },\n  { user: { name: 'John', age: undefined } }\n); // true\n\n// ES6 types are fully supported\nlooseEqual(new Map([[1, 2]]), new Map([[1, 2]])); // true\nlooseEqual(new Set([1, 2, 3]), new Set([3, 2, 1])); // true\n```\n\n## Comparison with other libraries\n\n| Scenario | `loose-deep-equal` | `fast-deep-equal` | `lodash.isEqual` |\n|----------|-------------------|-------------------|------------------|\n| `{a: 1}` vs `{a: 1, b: undefined}` | ✅ `true` | ❌ `false` | ❌ `false` |\n| `{a: null}` vs `{a: undefined}` | ❌ `false` | ❌ `false` | ❌ `false` |\n| Simple equal objects | 71.8% speed | 100% (baseline) | 13.4% speed |\n| Objects with undefined | 33.5% speed | 100% (baseline) | 19.0% speed |\n| Large objects (100+ props) | 96.5% speed | 100% (baseline) | 81.5% speed |\n| ES6 Maps/Sets support | ✅ Full | ✅ Full | ✅ Full |\n| TypedArray support | ✅ Full | ✅ Full | ✅ Full |\n\n## When to use this\n\nUse `loose-deep-equal` when:\n- Comparing API responses where fields might be omitted or explicitly set to `undefined`\n- Working with configuration objects with optional properties\n- Implementing state management where undefined and missing are semantically equivalent\n- Migrating between APIs that handle optional fields differently\n\n## When NOT to use this\n\nDon't use `loose-deep-equal` when:\n- You need to distinguish between missing properties and `undefined`\n- You're working with data where `undefined` has special meaning\n- You need strict equality semantics\n\n## How it works\n\nThe algorithm:\n1. If objects have the same number of keys, uses standard fast comparison\n2. If different number of keys, checks all properties from both objects\n3. Missing properties are treated as `undefined` during comparison\n4. Handles all standard JavaScript types including Date, RegExp, typed arrays, etc.\n\n## Performance\n\n`loose-deep-equal` is highly optimized for performance. Here are the benchmark results:\n\n### Performance vs fast-deep-equal\n\n| Scenario | Performance | Details |\n|----------|-------------|---------|\n| Simple equal objects | **71.8%** | Objects with same structure |\n| Nested objects | **85.1%** | Deep object hierarchies |\n| Large objects (100+ properties) | **96.5%** | Nearly identical performance |\n| Objects with undefined properties | **33.5%** | Our special case - checking all keys |\n\n### Key optimizations:\n- Fast path when objects have same number of keys (70-96% of original speed)\n- Efficient double-loop algorithm for different key counts\n- Early exit on first difference\n- No intermediate Set creation for key comparison\n- Native support for ES6 types without performance penalty\n\n### Benchmark details:\n- Still significantly faster than `lodash.isEqual` in all cases (3-6x faster)\n- Much faster than the `deep-equal` library (100-1000x faster)\n- Minimal overhead from Map/Set/TypedArray support\n\n## Edge cases handled\n\n- ✅ Objects with null prototype\n- ✅ Objects with overridden `hasOwnProperty`\n- ✅ Sparse arrays (holes treated as `undefined`)\n- ✅ Symbol properties (ignored, like other libraries)\n- ✅ Non-enumerable properties (ignored)\n- ⚠️  Arrays with extra properties: Only numeric indices are compared, extra properties are ignored. This matches `fast-deep-equal` behavior.\n  ```js\n  const arr1 = [1, 2, 3];\n  arr1.customProp = 'value1';\n  const arr2 = [1, 2, 3];\n  arr2.customProp = 'value2';\n  looseEqual(arr1, arr2); // true - extra properties ignored\n  ```\n\n## API\n\n### `looseEqual(a, b)`\n\nCompares two values for loose deep equality.\n\n**Parameters:**\n- `a` (*any*) - First value\n- `b` (*any*) - Second value\n\n**Returns:** `boolean` - True if values are loosely equal\n\n**Example:**\n```javascript\nconst looseEqual = require('loose-deep-equal');\n\nlooseEqual({ x: 1 }, { x: 1, y: undefined }); // true\nlooseEqual([1, 2, 3], [1, 2, 3]); // true\nlooseEqual(null, undefined); // false\n```\n\n## TypeScript\n\nTypeScript definitions are included:\n\n```typescript\nimport looseEqual from 'loose-deep-equal';\n\nconst result: boolean = looseEqual({ a: 1 }, { a: 1, b: undefined });\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT\n\n## Credits\n\nBased on the excellent [fast-deep-equal](https://github.com/epoberezkin/fast-deep-equal) by [@epoberezkin](https://github.com/epoberezkin).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglideapps%2Floose-deep-equal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglideapps%2Floose-deep-equal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglideapps%2Floose-deep-equal/lists"}