⚡ Fast, lightweight JavaScript utility for deep cloning, nested object updates, and immutable data manipulation..
Unlike other cloning libraries, smart-object-utils
automatically selects the optimal cloning strategy based on your data structure and runtime environment, ensuring maximum performance while handling edge cases that break other solutions.
It is a fast deep cloning & object manipulation toolkit, designed as a modern Lodash alternative with a performance-focused utility library approach.
Feature | smart-object-utils | lodash.cloneDeep | JSON.parse/stringify | structuredClone |
---|---|---|---|---|
Auto Strategy Selection | ✅ | ❌ | ❌ | ❌ |
Circular References | ✅ | ✅ | ❌ | ✅ |
Date/RegExp Support | ✅ | ✅ | ❌ | ✅ |
Map/Set Support | ✅ | ✅ | ❌ | ✅ |
Bundle Size | 🟢 4KB | 🟡 24KB | 🟢 0KB | 🟢 0KB |
Browser Compatibility | ✅ All | ✅ All | ✅ All | 🟡 Modern only |
Nested Updates | ✅ | ❌ | ❌ | ❌ |
TypeScript Support | ✅ | ✅ | ✅ |
npm install smart-object-utils
yarn add smart-object-utils
pnpm add smart-object-utils
import { deepClone, updateNested, deepMerge } from 'smart-object-utils'; // Clone any complex object const userSettings = deepClone(originalSettings); // Update nested properties immutably const updatedConfig = updateNested(config, 'database.connection.timeout', 5000); // Deep merge configurations const finalConfig = deepMerge(defaultConfig, userConfig);
Creates a deep copy of the given object using the optimal strategy.
const cloned = deepClone(originalObject, { strategy: 'auto', // 'auto' | 'json' | 'structured' | 'recursive' maxDepth: Infinity // Maximum clone depth });
Strategies:
auto
: Automatically selects best strategy (default)json
: Fast JSON-based cloning (limited types)structured
: ModernstructuredClone
(comprehensive)recursive
: Custom recursive cloning (most compatible)
Updates a nested property immutably.
const updated = updateNested(user, 'profile.settings.theme', 'dark', { clone: true // Whether to clone the object (default: true) });
Updates multiple nested properties at once.
const updated = updateMultiple(config, { 'database.host': 'localhost', 'api.timeout': 5000, 'logging.level': 'debug' });
Deep merges two objects.
const merged = deepMerge(defaultConfig, userConfig, { arrayStrategy: 'replace' // 'replace' | 'merge' });
Type checking utilities for robust object handling.
Path-based object property utilities.
// Benchmark against popular libraries (1000 iterations) const complexObject = { users: Array(1000).fill().map((_, i) => ({ id: i, profile: { name: `User ${i}`, settings: { theme: 'light' } } })) }; // Results (lower is better): // smart-object-utils (auto): 12ms // smart-object-utils (json): 8ms // lodash.cloneDeep: 45ms // JSON.parse/stringify: 15ms (but loses functions/dates) // structuredClone: 10ms (but limited browser support)
import { deepClone, JsonStrategy, RecursiveStrategy } from 'smart-object-utils'; // Force specific strategy const fastClone = deepClone(data, { strategy: 'json' }); const safeClone = deepClone(data, { strategy: 'recursive' }); // Check strategy availability if (JsonStrategy.canHandle(myObject)) { console.log('Object is JSON-serializable'); }
import { deepClone } from 'smart-object-utils'; try { const cloned = deepClone(complexObject); } catch (error) { if (error.message.includes('Circular reference')) { // Handle circular references } else if (error.message.includes('non-serializable')) { // Handle non-serializable values } }
Environment | Support | Notes |
---|---|---|
Node.js | 14+ | Full feature support |
Chrome | 60+ | Modern features available |
Firefox | 55+ | Modern features available |
Safari | 12+ | Modern features available |
IE | ❌ | Use Babel for transpilation |
React Native | ✅ | Full support |
# Run all tests npm test # Watch mode npm run test:watch # Coverage report npm run test:coverage
import { deepClone, deepMerge, updateNested } from 'smart-object-utils'; // Multi-environment configuration management const baseConfig = { database: { connection: { host: 'localhost', port: 5432, ssl: false, pool: { min: 2, max: 10 } }, migrations: { directory: './migrations', tableName: 'knex_migrations' } }, api: { rateLimiting: { windowMs: 15 * 60 * 1000, // 15 minutes maxRequests: 100 }, cors: { origin: ['http://localhost:3000'], credentials: true } }, logging: { level: 'info', transports: ['console', 'file'] } }; // Production environment overrides const productionOverrides = { database: { connection: { host: 'prod-db.example.com', ssl: true, pool: { min: 5, max: 50 } } }, api: { cors: { origin: ['https://app.example.com', 'https://admin.example.com'] } }, logging: { level: 'error', transports: ['file', 'elasticsearch'] } }; // Create environment-specific configurations without mutating base const productionConfig = deepMerge(baseConfig, productionOverrides, { arrayStrategy: 'replace' // Replace arrays instead of merging }); // Runtime configuration updates const updatedConfig = updateNested( productionConfig, 'database.connection.pool.max', 100 ); console.log('Base config unchanged:', baseConfig.database.connection.pool.max); // 10 console.log('Production config updated:', updatedConfig.database.connection.pool.max); // 100
import { deepClone, updateNested, updateMultiple } from 'smart-object-utils'; // Complex shopping cart state const cartState = { user: { id: 'user_123', profile: { name: 'John Doe', email: 'john@example.com', preferences: { currency: 'USD', newsletter: true } } }, cart: { items: [ { id: 'prod_001', name: 'MacBook Pro 16"', price: 2499.99, quantity: 1, options: { color: 'Space Gray', memory: '32GB', storage: '1TB' } }, { id: 'prod_002', name: 'Magic Mouse', price: 99.99, quantity: 2, options: { color: 'White' } } ], totals: { subtotal: 2699.97, tax: 216.00, shipping: 0, total: 2915.97 }, shipping: { address: { street: '123 Main St', city: 'San Francisco', zipCode: '94105' }, method: 'express' } }, ui: { currentStep: 'checkout', loading: false, errors: {} } }; // Redux-style immutable updates function cartReducer(state, action) { switch (action.type) { case 'UPDATE_QUANTITY': return updateNested( state, `cart.items[${action.itemIndex}].quantity`, action.quantity ); case 'UPDATE_SHIPPING_ADDRESS': return updateMultiple(state, { 'cart.shipping.address.street': action.address.street, 'cart.shipping.address.city': action.address.city, 'cart.shipping.address.zipCode': action.address.zipCode, 'ui.currentStep': 'payment' }); case 'ADD_ITEM': const currentItems = state.cart.items; const updatedItems = [...currentItems, action.item]; return updateNested(state, 'cart.items', updatedItems); case 'CLONE_CART_FOR_USER': // Clone cart for different user (wishlist, save for later, etc.) const clonedCart = deepClone(state.cart); return { ...state, savedCarts: [ ...(state.savedCarts || []), { ...clonedCart, savedAt: new Date(), userId: action.userId } ] }; default: return state; } } // Usage examples const newState1 = cartReducer(cartState, { type: 'UPDATE_QUANTITY', itemIndex: 0, quantity: 2 }); const newState2 = cartReducer(cartState, { type: 'UPDATE_SHIPPING_ADDRESS', address: { street: '456 Oak Ave', city: 'New York', zipCode: '10001' } });
import { deepClone, updateNested, deepMerge } from 'smart-object-utils'; import { useState, useCallback } from 'react'; // Complex dashboard configuration const defaultDashboardConfig = { layout: { grid: { columns: 12, gap: 16, responsive: true }, widgets: [ { id: 'sales-chart', type: 'chart', position: { x: 0, y: 0, w: 6, h: 4 }, config: { title: 'Sales Overview', chartType: 'line', dataSource: 'sales_api', refreshInterval: 300, colors: ['#3B82F6', '#10B981', '#F59E0B'] } }, { id: 'user-stats', type: 'metric', position: { x: 6, y: 0, w: 3, h: 2 }, config: { title: 'Active Users', metric: 'user_count', format: 'number', color: '#8B5CF6' } }, { id: 'recent-orders', type: 'table', position: { x: 0, y: 4, w: 12, h: 6 }, config: { title: 'Recent Orders', dataSource: 'orders_api', columns: ['id', 'customer', 'amount', 'status'], sortable: true, filterable: true } } ] }, theme: { mode: 'light', primaryColor: '#3B82F6', fontFamily: 'Inter', borderRadius: 8 }, features: { realTimeUpdates: true, notifications: { enabled: true, sound: false, position: 'top-right' }, exports: { pdf: true, excel: true, csv: true } } }; // Custom hook for dashboard configuration management function useDashboardConfig(initialConfig = defaultDashboardConfig) { const [config, setConfig] = useState(() => deepClone(initialConfig)); const updateWidgetConfig = useCallback((widgetId, configPath, value) => { setConfig(currentConfig => { const widgetIndex = currentConfig.layout.widgets.findIndex(w => w.id === widgetId); if (widgetIndex === -1) return currentConfig; return updateNested( currentConfig, `layout.widgets[${widgetIndex}].config.${configPath}`, value ); }); }, []); const addWidget = useCallback((widget) => { setConfig(currentConfig => { const newWidgets = [...currentConfig.layout.widgets, widget]; return updateNested(currentConfig, 'layout.widgets', newWidgets); }); }, []); const removeWidget = useCallback((widgetId) => { setConfig(currentConfig => { const filteredWidgets = currentConfig.layout.widgets.filter(w => w.id !== widgetId); return updateNested(currentConfig, 'layout.widgets', filteredWidgets); }); }, []); const updateTheme = useCallback((themeUpdates) => { setConfig(currentConfig => deepMerge(currentConfig, { theme: themeUpdates }) ); }, []); const cloneConfigForUser = useCallback((userId) => { return { userId, config: deepClone(config), createdAt: new Date() }; }, [config]); const resetToDefault = useCallback(() => { setConfig(deepClone(defaultDashboardConfig)); }, []); return { config, updateWidgetConfig, addWidget, removeWidget, updateTheme, cloneConfigForUser, resetToDefault }; } // Usage in React component function DashboardSettings() { const { config, updateWidgetConfig, updateTheme, cloneConfigForUser } = useDashboardConfig(); const handleChartColorChange = (color) => { updateWidgetConfig('sales-chart', 'colors[0]', color); }; const handleThemeToggle = () => { updateTheme({ mode: config.theme.mode === 'light' ? 'dark' : 'light' }); }; const saveAsTemplate = () => { const template = cloneConfigForUser('template_' + Date.now()); // Save template to backend saveTemplate(template); }; return ( <div> <button onClick={handleThemeToggle}> Switch to {config.theme.mode === 'light' ? 'Dark' : 'Light'} Mode </button> <button onClick={saveAsTemplate}> Save as Template </button> {/* Dashboard UI components */} </div> ); }
import { deepClone, updateNested, deepMerge } from 'smart-object-utils'; // Transform complex API responses for different UI contexts const rawApiResponse = { user_data: { user_id: 12345, first_name: 'Sarah', last_name: 'Johnson', email_address: 'sarah.johnson@company.com', profile_settings: { theme_preference: 'dark', notification_settings: { email_notifications: true, push_notifications: false, sms_notifications: true } }, account_details: { subscription_type: 'premium', billing_cycle: 'monthly', next_billing_date: '2024-02-15T00:00:00Z' } }, organization_data: { org_id: 'org_789', company_name: 'Tech Solutions Inc.', industry_type: 'software', employee_count: 150, departments: [ { dept_id: 1, name: 'Engineering', head_count: 45 }, { dept_id: 2, name: 'Sales', head_count: 25 }, { dept_id: 3, name: 'Marketing', head_count: 15 } ] }, permissions: { can_read: ['users', 'reports', 'dashboard'], can_write: ['users', 'dashboard'], can_delete: ['dashboard'], admin_access: false } }; // Transform for different UI contexts class ApiTransformer { static transformForUserProfile(apiData) { const cloned = deepClone(apiData); // Transform user data structure for profile UI return { id: cloned.user_data.user_id, displayName: `${cloned.user_data.first_name} ${cloned.user_data.last_name}`, email: cloned.user_data.email_address, preferences: { theme: cloned.user_data.profile_settings.theme_preference, notifications: { email: cloned.user_data.profile_settings.notification_settings.email_notifications, push: cloned.user_data.profile_settings.notification_settings.push_notifications, sms: cloned.user_data.profile_settings.notification_settings.sms_notifications } }, subscription: { type: cloned.user_data.account_details.subscription_type, billingCycle: cloned.user_data.account_details.billing_cycle, nextBilling: new Date(cloned.user_data.account_details.next_billing_date) }, organization: { id: cloned.organization_data.org_id, name: cloned.organization_data.company_name, industry: cloned.organization_data.industry_type }, permissions: cloned.permissions }; } static transformForAdminDashboard(apiData) { const cloned = deepClone(apiData); return { user: { id: cloned.user_data.user_id, name: `${cloned.user_data.first_name} ${cloned.user_data.last_name}`, email: cloned.user_data.email_address, accountType: cloned.user_data.account_details.subscription_type }, organization: { id: cloned.organization_data.org_id, name: cloned.organization_data.company_name, industry: cloned.organization_data.industry_type, size: cloned.organization_data.employee_count, departments: cloned.organization_data.departments.map(dept => ({ id: dept.dept_id, name: dept.name, headCount: dept.head_count })) }, access: { level: cloned.permissions.admin_access ? 'admin' : 'user', capabilities: { read: cloned.permissions.can_read, write: cloned.permissions.can_write, delete: cloned.permissions.can_delete } } }; } static transformForMobileApp(apiData) { const cloned = deepClone(apiData); // Simplified structure for mobile return { user: { id: cloned.user_data.user_id, displayName: `${cloned.user_data.first_name} ${cloned.user_data.last_name}`, email: cloned.user_data.email_address, isPremium: cloned.user_data.account_details.subscription_type === 'premium' }, settings: { theme: cloned.user_data.profile_settings.theme_preference, notifications: { push: cloned.user_data.profile_settings.notification_settings.push_notifications, sms: cloned.user_data.profile_settings.notification_settings.sms_notifications } }, company: { name: cloned.organization_data.company_name, size: cloned.organization_data.employee_count } }; } } // Usage in different contexts const profileData = ApiTransformer.transformForUserProfile(rawApiResponse); const adminData = ApiTransformer.transformForAdminDashboard(rawApiResponse); const mobileData = ApiTransformer.transformForMobileApp(rawApiResponse); // All transformations are independent - no cross-contamination console.log('Original API response unchanged'); console.log('Profile transformation:', profileData.displayName); console.log('Admin transformation:', adminData.organization.departments.length); console.log('Mobile transformation:', mobileData.user.isPremium);
- Inspired by Lodash for utility patterns
- Built with modern JavaScript standards
- Tested across multiple environments for reliability