Lodash Helpers
Handwritten Lodash function replacements for common object and array operations in the WorkPayCore frontend application.
Overview
The Lodash helpers provide lightweight implementations of popular Lodash functions to reduce bundle size while maintaining functionality.
Object Utilities
omit(object, paths)
Creates a new object with specified properties removed.
Parameters:
object(object): The source objectpaths(array, optional): Array of property names to omit (default:[])
Returns:
object: New object without the omitted properties
Example:
import { omit } from '@/utils/helpers/lodash';
const user = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
password: 'secret123',
role: 'admin',
};
// Remove sensitive data
const publicUser = omit(user, ['password']);
console.log(publicUser);
// Returns: { id: 1, name: 'John Doe', email: 'john@example.com', role: 'admin' }
// Remove multiple properties
const basicUser = omit(user, ['password', 'role']);
console.log(basicUser);
// Returns: { id: 1, name: 'John Doe', email: 'john@example.com' }
// Handle null/undefined objects
const safeOmit = omit(null, ['property']);
console.log(safeOmit);
// Returns: {}
Use Cases:
- Removing sensitive data from API responses
- Creating clean data for frontend display
- Preparing data for different user roles
- Sanitizing objects before storage
Array Utilities
getFirstItemFromArray(items)
Safely gets the first item from an array or array-like value.
Parameters:
items(any): Array, single item, or any value to extract first item from
Returns:
any: The first item or undefined if empty
Example:
import { getFirstItemFromArray } from '@/utils/helpers/lodash';
// Regular array
getFirstItemFromArray([1, 2, 3]); // Returns: 1
getFirstItemFromArray(['a', 'b', 'c']); // Returns: 'a'
// Single item (not array)
getFirstItemFromArray('hello'); // Returns: 'hello'
getFirstItemFromArray(42); // Returns: 42
// Empty array
getFirstItemFromArray([]); // Returns: undefined
// Null/undefined
getFirstItemFromArray(null); // Returns: undefined
getFirstItemFromArray(undefined); // Returns: undefined
Use Cases:
- Safe array access without errors
- Handling both single items and arrays
- Default value extraction
- API response processing
uniqBy(array, iteratee)
Creates a duplicate-free version of an array based on a property or function.
Parameters:
array(array, optional): The array to inspect (default:[])iteratee(string | function): Property name or function to determine uniqueness
Returns:
array: New array with unique values
Example:
import { uniqBy } from '@/utils/helpers/lodash';
// Using property name
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 1, name: 'John Duplicate' },
{ id: 3, name: 'Bob' },
];
const uniqueUsers = uniqBy(users, 'id');
console.log(uniqueUsers);
// Returns: [
// { id: 1, name: 'John' },
// { id: 2, name: 'Jane' },
// { id: 3, name: 'Bob' }
// ]
// Using function
const products = [
{ name: 'Laptop', category: 'Electronics', price: 1000 },
{ name: 'Mouse', category: 'Electronics', price: 25 },
{ name: 'Desk', category: 'Furniture', price: 300 },
{ name: 'Chair', category: 'Furniture', price: 200 },
];
const uniqueCategories = uniqBy(products, item => item.category);
console.log(uniqueCategories);
// Returns: [
// { name: 'Laptop', category: 'Electronics', price: 1000 },
// { name: 'Desk', category: 'Furniture', price: 300 }
// ]
// Handle empty arrays
const emptyResult = uniqBy([], 'id');
console.log(emptyResult);
// Returns: []
Use Cases:
- Removing duplicate records
- Unique user lists
- Deduplicating API responses
- Creating unique option lists
sortByKey(collection, key, order?)
Sorts an array of objects by a specified property.
Parameters:
collection(T[]): Array of objects to sortkey(keyof T): Property name to sort byorder(SortOrder, optional): 'asc' or 'desc' (default: 'asc')
Returns:
T[]: New sorted array
Example:
import { sortByKey } from '@/utils/helpers/lodash';
const employees = [
{ name: 'John', age: 30, salary: 75000 },
{ name: 'Jane', age: 25, salary: 80000 },
{ name: 'Bob', age: 35, salary: 70000 },
{ name: 'Alice', age: 28, salary: 85000 },
];
// Sort by age (ascending)
const sortedByAge = sortByKey(employees, 'age');
console.log(sortedByAge);
// Returns: [
// { name: 'Jane', age: 25, salary: 80000 },
// { name: 'Alice', age: 28, salary: 85000 },
// { name: 'John', age: 30, salary: 75000 },
// { name: 'Bob', age: 35, salary: 70000 }
// ]
// Sort by salary (descending)
const sortedBySalary = sortByKey(employees, 'salary', 'desc');
console.log(sortedBySalary);
// Returns: [
// { name: 'Alice', age: 28, salary: 85000 },
// { name: 'Jane', age: 25, salary: 80000 },
// { name: 'John', age: 30, salary: 75000 },
// { name: 'Bob', age: 35, salary: 70000 }
// ]
// Sort by name (alphabetical)
const sortedByName = sortByKey(employees, 'name');
console.log(sortedByName);
// Returns: [
// { name: 'Alice', age: 28, salary: 85000 },
// { name: 'Bob', age: 35, salary: 70000 },
// { name: 'Jane', age: 25, salary: 80000 },
// { name: 'John', age: 30, salary: 75000 }
// ]
Use Cases:
- Table sorting functionality
- Data presentation ordering
- Search result ranking
- Report generation
Advanced Usage Examples
Data Sanitization Pipeline
import { omit, uniqBy, sortByKey } from '@/utils/helpers/lodash';
const sanitizeAndProcessUsers = rawUsers => {
// Step 1: Remove sensitive fields
const publicUsers = rawUsers.map(user =>
omit(user, ['password', 'internalNotes', 'ssn']),
);
// Step 2: Remove duplicates by email
const uniqueUsers = uniqBy(publicUsers, 'email');
// Step 3: Sort by creation date (newest first)
const sortedUsers = sortByKey(uniqueUsers, 'createdAt', 'desc');
return sortedUsers;
};
// Usage
const rawData = [
{
id: 1,
email: 'john@example.com',
name: 'John',
password: 'secret',
createdAt: '2024-01-15',
},
{
id: 2,
email: 'jane@example.com',
name: 'Jane',
password: 'secret2',
createdAt: '2024-01-10',
},
{
id: 3,
email: 'john@example.com',
name: 'John Duplicate',
password: 'secret3',
createdAt: '2024-01-20',
},
];
const cleanUsers = sanitizeAndProcessUsers(rawData);
// Returns clean, unique, sorted user data
Table Sorting Hook
import { sortByKey } from '@/utils/helpers/lodash';
const useTableSort = (initialData = []) => {
const [data, setData] = useState(initialData);
const [sortConfig, setSortConfig] = useState({
key: null,
direction: 'asc',
});
const handleSort = key => {
let direction = 'asc';
if (sortConfig.key === key && sortConfig.direction === 'asc') {
direction = 'desc';
}
const sortedData = sortByKey([...data], key, direction);
setData(sortedData);
setSortConfig({ key, direction });
};
const resetSort = () => {
setData(initialData);
setSortConfig({ key: null, direction: 'asc' });
};
return {
data,
sortConfig,
handleSort,
resetSort,
setData,
};
};
// Usage in component
const EmployeeTable = ({ employees }) => {
const { data, sortConfig, handleSort } = useTableSort(employees);
return (
<table>
<thead>
<tr>
<th onClick={() => handleSort('name')}>
Name{' '}
{sortConfig.key === 'name' &&
(sortConfig.direction === 'asc' ? '↑' : '↓')}
</th>
<th onClick={() => handleSort('department')}>
Department{' '}
{sortConfig.key === 'department' &&
(sortConfig.direction === 'asc' ? '↑' : '↓')}
</th>
<th onClick={() => handleSort('salary')}>
Salary{' '}
{sortConfig.key === 'salary' &&
(sortConfig.direction === 'asc' ? '↑' : '↓')}
</th>
</tr>
</thead>
<tbody>
{data.map(employee => (
<tr key={employee.id}>
<td>{employee.name}</td>
<td>{employee.department}</td>
<td>{employee.salary}</td>
</tr>
))}
</tbody>
</table>
);
};
API Response Processing
import { omit, uniqBy, getFirstItemFromArray } from '@/utils/helpers/lodash';
const processApiResponse = response => {
// Get data from potentially nested response
const data = getFirstItemFromArray(response.data || response.results || []);
if (Array.isArray(data)) {
// Process array of items
return data
.map(item => omit(item, ['_internal', 'debug']))
.filter(item => item.active !== false);
}
// Process single item
return omit(data, ['_internal', 'debug']);
};
// Usage with error handling
const fetchAndProcessData = async endpoint => {
try {
const response = await fetch(endpoint);
const rawData = await response.json();
return processApiResponse(rawData);
} catch (error) {
console.error('API processing failed:', error);
return [];
}
};
Form Data Processing
import { omit, sortByKey } from '@/utils/helpers/lodash';
const useFormProcessor = () => {
const cleanFormData = formData => {
// Remove empty fields and internal form state
return omit(formData, ['__meta', '__errors', '__touched']);
};
const processSelectOptions = options => {
// Remove duplicates and sort options
const uniqueOptions = uniqBy(options, 'value');
return sortByKey(uniqueOptions, 'label');
};
const prepareForSubmission = formData => {
const cleaned = cleanFormData(formData);
// Convert empty strings to null
return Object.fromEntries(
Object.entries(cleaned).map(([key, value]) => [
key,
value === '' ? null : value,
]),
);
};
return {
cleanFormData,
processSelectOptions,
prepareForSubmission,
};
};
Data Grouping and Organization
import { uniqBy, sortByKey } from '@/utils/helpers/lodash';
const organizeEmployeeData = employees => {
// Get unique departments
const departments = uniqBy(employees, 'department').map(emp => ({
name: emp.department,
id: emp.departmentId,
}));
// Sort departments alphabetically
const sortedDepartments = sortByKey(departments, 'name');
// Group employees by department
return sortedDepartments.map(dept => ({
...dept,
employees: sortByKey(
employees.filter(emp => emp.department === dept.name),
'lastName',
),
}));
};
// Usage in component
const DepartmentView = ({ employees }) => {
const departments = useMemo(
() => organizeEmployeeData(employees),
[employees],
);
return (
<div>
{departments.map(dept => (
<div key={dept.id}>
<h3>{dept.name}</h3>
<ul>
{dept.employees.map(emp => (
<li key={emp.id}>
{emp.firstName} {emp.lastName}
</li>
))}
</ul>
</div>
))}
</div>
);
};
Performance Considerations
- Memory Efficiency: All functions create new objects/arrays instead of mutating
- Type Safety: Functions preserve TypeScript types where possible
- Bundle Size: Lightweight implementations reduce overall bundle size
- Browser Support: Compatible with modern browser environments
Comparison with Full Lodash
| Function | Lodash Size | Our Implementation | Features |
|---|---|---|---|
omit | ~4KB | ~100B | Basic property omission |
uniqBy | ~3KB | ~200B | Property/function-based uniqueness |
sortBy | ~2KB | ~150B | Basic sorting with direction |
head | ~1KB | ~50B | Safe first element access |
Best Practices
- Type Safety: Use TypeScript interfaces for better type checking
- Null Safety: Functions handle null/undefined inputs gracefully
- Immutability: All functions return new objects/arrays
- Performance: Consider memoization for expensive operations
- Testing: Test edge cases like empty arrays and null values
Related Utilities
- Object & Array Utilities - For more complex data operations
- String Utilities - For text processing
- General Helpers - For common utilities
TypeScript Definitions
type SortOrder = 'asc' | 'desc';
export function omit(o: any, paths?: string[]): any;
export function getFirstItemFromArray<T>(items: T | T[]): T | undefined;
export function uniqBy<T>(arr: T[], iteratee: string | ((item: T) => any)): T[];
export function sortByKey<T>(
collection: T[],
key: keyof T,
order?: SortOrder,
): T[];
Browser Compatibility
- ES6+: Uses modern JavaScript features
- Array Methods: Relies on native
filter,map,findIndex - Object Methods: Uses
Object.entries,Object.fromEntries - Spread Operator: Uses spread syntax for array/object cloning
Bundle Impact
Using these helpers instead of full Lodash can reduce bundle size by:
- Before: ~67KB (gzipped: ~25KB) for common Lodash functions
- After: ~1KB (gzipped: ~400B) for these implementations
- Savings: ~99% reduction in bundle size for these functions