Skip to main content

updateNestedProperties

An immutable utility function that updates nested object properties using dot notation paths. It creates a new object with the updated value while preserving the original object's immutability, making it perfect for state management and functional programming patterns.


Problem it Solves

  • Immutable Updates: Update nested properties without mutating the original object
  • State Management: Safely update complex state structures in React/Vue applications
  • Configuration Updates: Modify nested configuration objects immutably
  • Data Transformation: Transform nested data structures without side effects
  • Functional Programming: Maintain immutability principles in data operations

Input/Output Details

Parameters

ParameterTypeRequiredDescription
objT extends Record<string, unknown>The object to update (will not be mutated)
keystringDot notation path to the property to update
newValueunknownThe new value to set at the specified path

Return Value

TypeDescription
TA new object with the updated property (original object remains unchanged)

Path String Syntax

The key parameter uses dot notation to specify nested property locations:

  • Single Level: "property" - Update a top-level property
  • Nested: "parent.child" - Update a nested property
  • Deep Nested: "level1.level2.level3" - Update deeply nested properties
  • Auto-Creation: Missing intermediate objects are created automatically

Basic Usage

import { updateNestedProperties } from "@kousta-ui/helpers";

const user = {
personal: {
first_name: "John",
last_name: "Doe"
},
contact: {
email: "john@example.com"
}
};

// Update top-level property
const updated1 = updateNestedProperties(user, "contact.email", "newemail@example.com");
console.log(updated1.contact.email); // "newemail@example.com"
console.log(user.contact.email); // "john@example.com" (original unchanged)

// Update nested property
const updated2 = updateNestedProperties(user, "personal.first_name", "Jane");
console.log(updated2.personal.first_name); // "Jane"

// Create new nested property
const updated3 = updateNestedProperties(user, "contact.phone", "123-456-7890");
console.log(updated3.contact.phone); // "123-456-7890"

Examples

React State Management

import React, { useState } from "react";
import { updateNestedProperties } from "@kousta-ui/helpers";

function UserProfile() {
const [user, setUser] = useState({
personal: {
first_name: "John",
last_name: "Doe"
},
contact: {
email: "john@example.com",
phone: ""
},
preferences: {
theme: "light",
notifications: true
}
});

const updateEmail = (newEmail) => {
setUser(prevUser =>
updateNestedProperties(prevUser, "contact.email", newEmail)
);
};

const updateTheme = (newTheme) => {
setUser(prevUser =>
updateNestedProperties(prevUser, "preferences.theme", newTheme)
);
};

const addPhone = (phoneNumber) => {
setUser(prevUser =>
updateNestedProperties(prevUser, "contact.phone", phoneNumber)
);
};

return (
<div>
<h1>{user.personal.first_name} {user.personal.last_name}</h1>
<input
value={user.contact.email}
onChange={(e) => updateEmail(e.target.value)}
placeholder="Email"
/>
<input
value={user.contact.phone}
onChange={(e) => addPhone(e.target.value)}
placeholder="Phone"
/>
<select
value={user.preferences.theme}
onChange={(e) => updateTheme(e.target.value)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}

Configuration Management

import { updateNestedProperties } from "@kousta-ui/helpers";

const defaultConfig = {
api: {
baseUrl: "https://api.example.com",
version: "v1",
timeout: 5000
},
database: {
host: "localhost",
port: 5432,
name: "myapp"
},
features: {
authentication: true,
logging: false,
analytics: true
}
};

// Update API configuration for production
const prodConfig = updateNestedProperties(defaultConfig, "api.baseUrl", "https://api.prod.example.com");
const prodConfigWithTimeout = updateNestedProperties(prodConfig, "api.timeout", 10000);

// Update database for production
const prodDbConfig = updateNestedProperties(prodConfigWithTimeout, "database.host", "prod-db.example.com");

// Enable logging for production
const finalProdConfig = updateNestedProperties(prodDbConfig, "features.logging", true);

console.log(finalProdConfig);
/*
{
api: {
baseUrl: "https://api.prod.example.com",
version: "v1",
timeout: 10000
},
database: {
host: "prod-db.example.com",
port: 5432,
name: "myapp"
},
features: {
authentication: true,
logging: true,
analytics: true
}
}
*/

Form Data Processing

import { updateNestedProperties } from "@kousta-ui/helpers";

const initialFormData = {
user: {
personal: {},
contact: {}
},
preferences: {}
};

// Process form submission
function processFormSubmission(formData) {
let updatedData = { ...formData };

// Update personal information
updatedData = updateNestedProperties(updatedData, "user.personal.first_name", "John");
updatedData = updateNestedProperties(updatedData, "user.personal.last_name", "Doe");
updatedData = updateNestedProperties(updatedData, "user.personal.age", 30);

// Update contact information
updatedData = updateNestedProperties(updatedData, "user.contact.email", "john@example.com");
updatedData = updateNestedProperties(updatedData, "user.contact.phone", "123-456-7890");

// Update preferences
updatedData = updateNestedProperties(updatedData, "preferences.theme", "dark");
updatedData = updateNestedProperties(updatedData, "preferences.notifications", true);

return updatedData;
}

const processedData = processFormSubmission(initialFormData);
console.log(processedData);
/*
{
user: {
personal: {
first_name: "John",
last_name: "Doe",
age: 30
},
contact: {
email: "john@example.com",
phone: "123-456-7890"
}
},
preferences: {
theme: "dark",
notifications: true
}
}
*/

Advanced Examples

E-commerce Product Updates

import { updateNestedProperties } from "@kousta-ui/helpers";

const product = {
id: "prod_123",
name: "Wireless Headphones",
pricing: {
currency: "USD",
amounts: {
regular: 99.99,
sale: 79.99
}
},
inventory: {
warehouse: {
location: "NYC",
quantity: 150
}
},
metadata: {}
};

// Update sale price
const updatedPrice = updateNestedProperties(product, "pricing.amounts.sale", 69.99);

// Update inventory quantity
const updatedInventory = updateNestedProperties(updatedPrice, "inventory.warehouse.quantity", 125);

// Add new metadata
const withMetadata = updateNestedProperties(updatedInventory, "metadata.last_updated", "2024-01-15");

// Add discount information
const finalProduct = updateNestedProperties(withMetadata, "metadata.discount_percentage", 30);

console.log(finalProduct);
/*
{
id: "prod_123",
name: "Wireless Headphones",
pricing: {
currency: "USD",
amounts: {
regular: 99.99,
sale: 69.99
}
},
inventory: {
warehouse: {
location: "NYC",
quantity: 125
}
},
metadata: {
last_updated: "2024-01-15",
discount_percentage: 30
}
}
*/

User Settings Management

import { updateNestedProperties } from "@kousta-ui/helpers";

const defaultSettings = {
profile: {
personal: {},
professional: {}
},
preferences: {
display: {},
notifications: {},
privacy: {}
},
security: {
authentication: {},
sessions: {}
}
};

// Update user profile
function updateUserProfile(settings, profileData) {
let updated = settings;

updated = updateNestedProperties(updated, "profile.personal.first_name", profileData.firstName);
updated = updateNestedProperties(updated, "profile.personal.last_name", profileData.lastName);
updated = updateNestedProperties(updated, "profile.personal.bio", profileData.bio);
updated = updateNestedProperties(updated, "profile.professional.title", profileData.title);
updated = updateNestedProperties(updated, "profile.professional.company", profileData.company);

return updated;
}

// Update preferences
function updatePreferences(settings, prefs) {
let updated = settings;

updated = updateNestedProperties(updated, "preferences.display.theme", prefs.theme);
updated = updateNestedProperties(updated, "preferences.display.language", prefs.language);
updated = updateNestedProperties(updated, "preferences.notifications.email", prefs.emailNotifications);
updated = updateNestedProperties(updated, "preferences.notifications.push", prefs.pushNotifications);
updated = updateNestedProperties(updated, "preferences.privacy.profile_visibility", prefs.profileVisibility);

return updated;
}

// Usage
const userProfile = {
firstName: "Alice",
lastName: "Johnson",
bio: "Software Developer",
title: "Senior Developer",
company: "Tech Corp"
};

const userPrefs = {
theme: "dark",
language: "en",
emailNotifications: true,
pushNotifications: false,
profileVisibility: "public"
};

let userSettings = defaultSettings;
userSettings = updateUserProfile(userSettings, userProfile);
userSettings = updatePreferences(userSettings, userPrefs);

console.log(userSettings);

Node.js Usage Example

const { updateNestedProperties } = require("@kousta-ui/helpers");
const fs = require("fs");

// Configuration management for Node.js application
class ConfigManager {
constructor(configPath) {
this.configPath = configPath;
this.config = this.loadConfig();
}

loadConfig() {
try {
const data = fs.readFileSync(this.configPath, "utf8");
return JSON.parse(data);
} catch (error) {
console.error("Failed to load config:", error);
return this.getDefaultConfig();
}
}

getDefaultConfig() {
return {
server: {
host: "localhost",
port: 3000
},
database: {
host: "localhost",
port: 5432,
name: "myapp"
},
logging: {
level: "info",
file: "app.log"
}
};
}

updateConfig(path, value) {
this.config = updateNestedProperties(this.config, path, value);
return this.saveConfig();
}

saveConfig() {
try {
fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
return true;
} catch (error) {
console.error("Failed to save config:", error);
return false;
}
}

getConfig(path) {
if (path) {
return this.getNestedProperty(this.config, path);
}
return this.config;
}
}

// Usage
const configManager = new ConfigManager("./config.json");

// Update server configuration
configManager.updateConfig("server.host", "0.0.0.0");
configManager.updateConfig("server.port", 8080);

// Update database configuration
configManager.updateConfig("database.host", "prod-db.example.com");
configManager.updateConfig("database.name", "production_db");

// Update logging configuration
configManager.updateConfig("logging.level", "warn");
configManager.updateConfig("logging.file", "production.log");

console.log("Updated configuration:", configManager.getConfig());

Edge Cases

Creating New Nested Paths

const obj = { existing: "value" };

// Creates nested structure automatically
const updated = updateNestedProperties(obj, "new.nested.path", "new value");
console.log(updated);
/*
{
existing: "value",
new: {
nested: {
path: "new value"
}
}
}
*/

Overwriting Different Types

const obj = {
config: "string_value"
};

// Overwrites string with object
const updated = updateNestedProperties(obj, "config.nested", "new value");
console.log(updated);
/*
{
config: {
nested: "new value"
}
}
*/

Handling Null/Undefined Values

const obj = {
existing: {
prop: "value"
}
};

// Can set null or undefined values
const updated1 = updateNestedProperties(obj, "existing.prop", null);
const updated2 = updateNestedProperties(updated1, "existing.newProp", undefined);

console.log(updated1); // { existing: { prop: null } }
console.log(updated2); // { existing: { prop: null, newProp: undefined } }

Performance Considerations

  • Immutability: Creates new objects, which has memory overhead
  • Deep Cloning: Only clones necessary parts of the object
  • Path Parsing: Efficient dot notation parsing
  • Object Creation: Minimal object creation for optimal performance

Performance Tip For frequent updates to the same object, consider batching updates or using a more specialized state management solution.


TypeScript Support

Full TypeScript support with generic types:

import { updateNestedProperties } from "@kousta-ui/helpers";

interface User {
personal: {
first_name: string;
last_name: string;
};
contact: {
email: string;
};
}

const user: User = {
personal: { first_name: "John", last_name: "Doe" },
contact: { email: "john@example.com" }
};

// Type-safe updates
const updatedUser: User = updateNestedProperties(user, "contact.email", "new@example.com");

// With explicit typing
const customUpdate: User = updateNestedProperties<User>(user, "personal.first_name", "Jane");

Common Pitfalls

Forgetting Immutability

// ❌ Wrong - expecting original to be mutated
const original = { a: { b: 1 } };
const updated = updateNestedProperties(original, "a.b", 2);
console.log(original.a.b); // Still 1, not 2

// ✅ Correct - use the returned object
console.log(updated.a.b); // 2

Complex Path Updates

// ❌ Wrong - multiple separate updates create multiple objects
let obj = { a: { b: { c: 1 } } };
obj = updateNestedProperties(obj, "a.b.c", 2);
obj = updateNestedProperties(obj, "a.b.d", 3);

// ✅ Better - batch updates when possible
const updates = [
{ path: "a.b.c", value: 2 },
{ path: "a.b.d", value: 3 }
];
// Apply updates in sequence

Migration from Manual Updates

Before Manual Updates

// Manual immutable updates
function updateUserEmail(user, newEmail) {
return {
...user,
contact: {
...user.contact,
email: newEmail
}
};
}

function updateNestedValue(obj, path, value) {
const keys = path.split(".");
const result = { ...obj };
let current = result;

for (let i = 0; i < keys.length - 1; i++) {
current[keys[i]] = { ...current[keys[i]] };
current = current[keys[i]];
}

current[keys[keys.length - 1]] = value;
return result;
}

After updateNestedProperties

import { updateNestedProperties } from "@kousta-ui/helpers";

// Simplified updates
function updateUserEmail(user, newEmail) {
return updateNestedProperties(user, "contact.email", newEmail);
}

// No need for manual deep cloning
const updated = updateNestedProperties(obj, "deep.nested.path", value);

Types (reference)

export const updateNestedProperties = <T extends Record<string, unknown>>(
obj: T,
key: string,
newValue: unknown
): T;