@chaisser/unique-by
v1.0.1
Published
Get unique array items by key or function
Maintainers
Readme
🎯 @chaisser/unique-by
Get unique array items by key or function — dedupe, diff, intersect, count, and more
✨ Features
- 🎯 Type-safe - Full TypeScript support with generics
- 🔑 Key-based dedup - Unique by property, function, or multiple props
- 🔁 First or last - Keep first or last occurrence
- 📊 Counts & frequencies - Count occurrences, compute frequencies
- ➖ Set operations - Difference, intersection, union, symmetric difference
- 🔍 Duplicate detection - Find duplicates, singletons, nth occurrences
- 🔄 Lazy iteration - Generator-based unique iteration
- ✅ Validation - Check uniqueness, count duplicates
- 🪶 Zero dependencies - Lightweight and tree-shakeable
- 🏎️ ESM + CJS - Dual module format support
📦 Installation
npm install @chaisser/unique-by
# or
yarn add @chaisser/unique-by
# or
pnpm add @chaisser/unique-by🚀 Quick Start
import {
unique,
uniqueBy,
uniqueByKey,
duplicates,
intersection,
union,
isUnique,
} from '@chaisser/unique-by';
// Simple unique
unique([1, 2, 2, 3, 3]); // [1, 2, 3]
// Unique by key function
uniqueBy([{ id: 1, name: 'a' }, { id: 2, name: 'b' }, { id: 1, name: 'c' }], item => item.id);
// [{ id: 1, name: 'a' }, { id: 2, name: 'b' }]
// Unique by object property
uniqueByKey([{ id: 1 }, { id: 2 }, { id: 1 }], 'id');
// [{ id: 1 }, { id: 2 }]
// Check uniqueness
isUnique([1, 2, 3]); // true
hasDuplicates([1, 2, 2]); // true📖 What It Does
This package provides a comprehensive set of array uniqueness utilities for JavaScript and TypeScript. It handles basic deduplication, key-based uniqueness, duplicate detection, set operations (difference, intersection, union), frequency counting, lazy iteration, and validation — all with full TypeScript generics.
🎯 How It Works
The package provides:
- Basic unique -
unique,uniqueBy,uniqueByKey - Last occurrence -
uniqueLast,uniqueLastBy,uniqueLastByKey - Duplicate detection -
duplicates,duplicatesBy,duplicatesByKey - Counting -
countBy,countByFn,frequencies,frequenciesBy - Grouping -
uniqueValues,uniqueValuesByKey - Set operations -
difference,differenceBy,symmetricDifference,intersection,intersectionBy,union,unionBy - Occurrence filtering -
singletons,singletonsBy,onlyDuplicates,onlyDuplicatesBy,nthOccurrence,nthOccurrenceBy - Index mapping -
uniqueIndexMap,uniqueIndexMapBy - Compact -
compact,compactUnique - Chunked unique -
uniqueChunks,uniqueChunksBy - Object arrays -
uniqueByProps,uniqueByAllProps - Lazy iteration -
uniqueIterable,uniqueIterableBy - Validation -
isUnique,isUniqueBy,hasDuplicates,hasDuplicatesBy,duplicateCount,duplicateCountBy - Sorting -
uniqueSorted,uniqueSortedBy
🎨 What It's Useful For
- Data deduplication - Remove duplicate records by ID or composite key
- Set operations - Compare datasets, find overlaps and differences
- Duplicate detection - Find and report duplicate entries
- Frequency analysis - Count how often values appear
- Data validation - Ensure array uniqueness before processing
- Lazy evaluation - Process large datasets without loading all into memory
💡 Usage Examples
Basic Deduplication
import { unique } from '@chaisser/unique-by';
unique([1, 2, 2, 3, 3, 3]); // [1, 2, 3]
unique(['a', 'b', 'a']); // ['a', 'b']
unique([]); // []Unique by Key Function
import { uniqueBy } from '@chaisser/unique-by';
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice (duplicate)' },
];
uniqueBy(users, user => user.id);
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]Unique by Object Property
import { uniqueByKey, uniqueByProps } from '@chaisser/unique-by';
const items = [
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 },
];
uniqueByKey(items, 'x'); // [{ x: 1, y: 1 }, { x: 1, y: 2 }]
uniqueByProps(items, ['x', 'y']); // [{ x: 1, y: 1 }, { x: 1, y: 2 }]Keep Last Occurrence
import { uniqueLast, uniqueLastBy } from '@chaisser/unique-by';
uniqueLast([1, 2, 1, 3, 2]); // [1, 3, 2]
const items = [{ id: 1, v: 'old' }, { id: 2, v: 'b' }, { id: 1, v: 'new' }];
uniqueLastBy(items, item => item.id);
// [{ id: 2, v: 'b' }, { id: 1, v: 'new' }]Duplicate Detection
import { duplicates, singletons, onlyDuplicates } from '@chaisser/unique-by';
duplicates([1, 2, 2, 3, 3, 3]); // [2, 2, 3, 3, 3]
singletons([1, 2, 2, 3, 4, 4]); // [1, 3]
onlyDuplicates([1, 2, 2, 3, 3, 4]); // [2, 2, 3, 3]Set Operations
import { difference, intersection, union, symmetricDifference } from '@chaisser/unique-by';
difference([1, 2, 3, 4], [2, 4]); // [1, 3]
intersection([1, 2, 3], [2, 3, 4]); // [2, 3]
union([1, 2, 3], [2, 3, 4]); // [1, 2, 3, 4]
symmetricDifference([1, 2, 3], [2, 3, 4]); // [1, 4]By-key set operations
import { differenceBy, intersectionBy, unionBy } from '@chaisser/unique-by';
const a = [{ id: 1 }, { id: 2 }, { id: 3 }];
const b = [{ id: 2 }, { id: 4 }];
differenceBy(a, b, item => item.id); // [{ id: 1 }, { id: 3 }]
intersectionBy(a, b, item => item.id); // [{ id: 2 }]
unionBy(a, b, item => item.id); // [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]Counting & Frequencies
import { countBy, frequencies, frequenciesBy } from '@chaisser/unique-by';
countBy(['a', 'b', 'a', 'c', 'a']); // Map { 'a' => 3, 'b' => 1, 'c' => 1 }
frequencies(['a', 'b', 'a']); // { a: 2, b: 1 }
frequenciesBy([{ type: 'x' }, { type: 'y' }, { type: 'x' }], item => item.type);
// { x: 2, y: 1 }Nth Occurrence
import { nthOccurrence, nthOccurrenceBy } from '@chaisser/unique-by';
nthOccurrence([1, 2, 1, 2, 1], 2); // [1, 2] (2nd occurrence)
nthOccurrence([1, 2, 3], 2); // [] (no 2nd occurrences)Compact (remove null/undefined)
import { compact, compactUnique } from '@chaisser/unique-by';
compact([1, null, 2, undefined, 3]); // [1, 2, 3]
compactUnique([1, null, 2, 2, 3]); // [1, 2, 3]Lazy Iteration
import { uniqueIterable, uniqueIterableBy } from '@chaisser/unique-by';
for (const item of uniqueIterable([1, 2, 2, 3, 1])) {
console.log(item); // 1, 2, 3
}Validation
import { isUnique, hasDuplicates, duplicateCount } from '@chaisser/unique-by';
isUnique([1, 2, 3]); // true
isUnique([1, 2, 2]); // false
hasDuplicates([1, 2, 2]); // true
duplicateCount([1, 2, 2, 3, 3, 3]); // 3Sorted Unique
import { uniqueSorted, uniqueSortedBy } from '@chaisser/unique-by';
uniqueSorted([3, 1, 2, 2, 1]); // [1, 2, 3]
const items = [{ v: 3 }, { v: 1 }, { v: 2 }, { v: 1 }];
uniqueSortedBy(items, item => item.v, (a, b) => a.v - b.v);
// [{ v: 1 }, { v: 2 }, { v: 3 }]Index Mapping
import { uniqueIndexMap } from '@chaisser/unique-by';
const map = uniqueIndexMap(['a', 'b', 'a', 'c']);
map.get('a'); // 0
map.get('b'); // 1
map.get('c'); // 3📚 API Reference
Basic Unique
| Function | Signature | Description |
|---|---|---|
| unique(array) | (T[]) → T[] | Remove duplicate primitives |
| uniqueBy(array, keyFn) | (T[], (T) => unknown) → T[] | Unique by key function, keeps first |
| uniqueByKey(array, key) | (Record[], keyof T) → T[] | Unique by object property |
Last Occurrence
| Function | Signature | Description |
|---|---|---|
| uniqueLast(array) | (T[]) → T[] | Keep last occurrence of each value |
| uniqueLastBy(array, keyFn) | (T[], (T) => unknown) → T[] | Keep last by key function |
| uniqueLastByKey(array, key) | (Record[], keyof T) → T[] | Keep last by object property |
Duplicate Detection
| Function | Description |
|---|---|
| duplicates(array) | Return all items that appear more than once |
| duplicatesBy(array, keyFn) | Duplicates by key function |
| duplicatesByKey(array, key) | Duplicates by object property |
Counting
| Function | Description |
|---|---|
| countBy(array) | Map of item to occurrence count |
| countByFn(array, keyFn) | Map of key to occurrence count |
| frequencies(array) | Object of stringified item to count |
| frequenciesBy(array, keyFn) | Object of stringified key to count |
Grouping
| Function | Description |
|---|---|
| uniqueValues(array, keyFn) | Group items by key function |
| uniqueValuesByKey(array, key) | Group items by object property |
Set Operations
| Function | Description |
|---|---|
| difference(a, b) | Items in a not in b |
| differenceBy(a, b, keyFn) | Difference by key function |
| symmetricDifference(a, b) | Items in either but not both |
| intersection(a, b) | Items in both arrays |
| intersectionBy(a, b, keyFn) | Intersection by key function |
| union(a, b) | Combined unique from both |
| unionBy(a, b, keyFn) | Union by key function |
Occurrence Filtering
| Function | Description |
|---|---|
| singletons(array) | Items that appear exactly once |
| singletonsBy(array, keyFn) | Singletons by key function |
| onlyDuplicates(array) | Items that appear more than once |
| onlyDuplicatesBy(array, keyFn) | Duplicates by key function |
| nthOccurrence(array, n) | Items at their nth occurrence |
| nthOccurrenceBy(array, keyFn, n) | Nth occurrence by key function |
Index Mapping
| Function | Description |
|---|---|
| uniqueIndexMap(array) | Map of item to its first index |
| uniqueIndexMapBy(array, keyFn) | Map of key to its first index |
Compact
| Function | Description |
|---|---|
| compact(array) | Remove null and undefined |
| compactUnique(array) | Compact then deduplicate |
Chunked Unique
| Function | Description |
|---|---|
| uniqueChunks(array, size) | Deduplicate within each chunk |
| uniqueChunksBy(array, size, keyFn) | Deduplicate chunks by key |
Object Arrays
| Function | Description |
|---|---|
| uniqueByProps(array, keys) | Unique by multiple properties |
| uniqueByAllProps(array) | Unique by all properties (JSON) |
Lazy Iteration
| Function | Description |
|---|---|
| uniqueIterable(iterable) | Generator yielding unique items |
| uniqueIterableBy(iterable, keyFn) | Generator yielding unique items by key |
Validation
| Function | Description |
|---|---|
| isUnique(array) | Check if array has no duplicates |
| isUniqueBy(array, keyFn) | Check uniqueness by key function |
| hasDuplicates(array) | Check if array has duplicates |
| hasDuplicatesBy(array, keyFn) | Check duplicates by key function |
| duplicateCount(array) | Number of duplicate items |
| duplicateCountBy(array, keyFn) | Duplicate count by key function |
Sorting
| Function | Description |
|---|---|
| uniqueSorted(array) | Sorted unique values |
| uniqueSortedBy(array, keyFn, compareFn?) | Unique with optional sort |
🔗 Related Packages
Explore our other utility packages in the @chaisser namespace:
- @chaisser/unique-by (this package) - Get unique array items by key or function
- @chaisser/chunk-array - Split arrays into chunks
- @chaisser/string-wizard - Advanced string manipulation
- @chaisser/type-guard - Runtime type guards and validators
- @chaisser/uuid-v7 - Time-ordered UUID v7 generator
- @chaisser/wait-for - Promise-based wait utilities
- @chaisser/regex-humanizer - Regex to human-readable descriptions
- @chaisser/password-strength - Password strength checker
- @chaisser/human-time - Human-readable time formatting
- @chaisser/obj-path - Safe dot-notation object access
- @chaisser/debounce-throttle - Rate limiting utilities
- @chaisser/color-utils - Color conversion utilities
- @chaisser/deep-clone - Deep cloning functions
- @chaisser/array-group-by - Array grouping utilities
- @chaisser/merge-objects - Object merge utilities
- @chaisser/event-emitter - Typed event emitter
🔒 License
MIT - Free to use in personal and commercial projects
👨 Developed by
Doruk Karaboncuk [email protected]
📄 Repository
- GitHub: @Chaisser
- NPM: @chaisser/unique-by
🤝 Contributing
Contributions are welcome! Feel free to:
- Report bugs
- Suggest new features
- Submit pull requests
- Improve documentation
📞 Support
For issues, questions, or suggestions, please reach out through:
- Email: [email protected]
- GitHub Issues: Create an issue
Made with ❤️ by @chaisser
