1.5 Array & Object Methods & Immutability
← Back to Chapter 1: JavaScript Fundamentals
In modern web development and React architecture, user interfaces are treated as a pure projection of application state (UI = f(state)). Mastering higher-order array methods, object transformations, shallow vs deep copying, and immutable state updates is essential for writing bug-free, high-performance applications.
1. Higher-Order Array Methods
A higher-order function is a function that accepts other functions as arguments or returns a function.
A. map(): Transforming Elements
Returns a brand new array by applying a transform callback to every element:
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8]
B. filter(): Selecting Subsets
Returns a brand new array containing only elements that satisfy the predicate check:
const tasks = [
{ id: 1, title: "Learn React", completed: true },
{ id: 2, title: "Master Closures", completed: false }
];
const pendingTasks = tasks.filter(task => !task.completed);
C. reduce(): Aggregating to Any Structure
Accumulates array items into a single final result (number, string, object, array, or lookup dictionary):
const cart = [
{ item: "Keyboard", price: 100, category: "Hardware" },
{ item: "Mouse", price: 50, category: "Hardware" },
{ item: "Course", price: 30, category: "Education" }
];
// Calculate total cart value
const totalPrice = cart.reduce((acc, curr) => acc + curr.price, 0); // 180
// Group items by category into a lookup dictionary
const grouped = cart.reduce((acc, curr) => {
const cat = curr.category;
acc[cat] = acc[cat] || [];
acc[cat].push(curr.item);
return acc;
}, {});
// { Hardware: ['Keyboard', 'Mouse'], Education: ['Course'] }
D. forEach() vs map()
forEach() executes a callback for every element for side-effects only and always returns undefined. map() returns a new transformed array.
| Feature | map() | forEach() |
|---|---|---|
| Return Value | Returns a new array | Always returns undefined |
| Chainable? | Yes (.map().filter()) | No |
| Primary Use Case | Pure data transformation (e.g. rendering JSX lists) | Side effects (logging, analytics, mutating external targets) |
| Usage in React JSX | Standard ({items.map(item => <Card key={item.id} />)}) | Cannot be used inside JSX expressions |
const users = ["Alice", "Bob"];
// forEach: for side effects
users.forEach(user => console.log(`Notifying ${user}`)); // returns undefined
// map: creates a new array of objects
const userObjects = users.map(name => ({ name, role: "Member" }));
E. Search & Verification Methods
find(predicate): Returns the first element that satisfies the condition, orundefined.const user = users.find(u => u.id === 42);findIndex(predicate): Returns the index of the first match, or-1.some(predicate): Returnstrueif at least one element passes the check.const hasAdmin = users.some(u => u.role === "admin");every(predicate): Returnstrueif all elements pass the check.const allActive = users.every(u => u.isActive);
2. Object Methods
JavaScript provides powerful built-in static methods on Object for transforming and inspecting objects.
A. Object.keys(), Object.values(), and Object.entries()
const stats = { speed: 95, health: 120, attack: 85 };
console.log(Object.keys(stats)); // ['speed', 'health', 'attack']
console.log(Object.values(stats)); // [95, 120, 85]
console.log(Object.entries(stats));
// [['speed', 95], ['health', 120], ['attack', 85]]
B. Object.fromEntries(): Rebuilding Objects
Converts key-value pairs back into an object, making it easy to filter or map over object properties:
// Filter an object to retain only stats with value >= 90
const eliteStats = Object.fromEntries(
Object.entries(stats).filter(([key, value]) => value >= 90)
);
console.log(eliteStats); // { speed: 95, health: 120 }
C. Object.assign()
Copies all enumerable own properties from one or more source objects to a target object:
const target = { a: 1 };
const source = { b: 2, c: 3 };
const merged = Object.assign({}, target, source); // { a: 1, b: 2, c: 3 }
D. Object.freeze() vs Object.seal()
Object.freeze(obj): Completely freezes an object (shallow). Properties cannot be added, removed, or modified.Object.seal(obj): Prevents adding or removing properties, but existing writable properties can still be modified.
3. Shallow Copy vs Deep Copy
Because objects and arrays in JavaScript are reference types stored on the heap, understanding how copies work is crucial to preventing unintended mutations.
graph TD
subgraph Shallow Copy [Spread / Object.assign]
O1[Original Object] --> P1[Primitive Value: name]
O1 --> N1[Nested Object: address 0xAA01]
C1[Copied Object] --> P2[Primitive Value: name (independent)]
C1 --> N1
end
subgraph Deep Copy [structuredClone]
O2[Original Object] --> P3[Primitive Value: name]
O2 --> N2[Nested Object: address 0xBB01]
C2[Deep Cloned Object] --> P4[Primitive Value: name]
C2 --> N3[Cloned Nested Object: address 0xCC02 (independent)]
end
The Shallow Copy Trap
Spread ({ ...obj }), Object.assign({}, obj), and [...arr] only clone the first layer of properties:
const original = {
name: "Sarwvidya",
settings: { theme: "dark" }
};
const shallowCopy = { ...original };
shallowCopy.name = "Alex"; // Safe: primitive string is independent
shallowCopy.settings.theme = "light"; // ❌ BUG: original.settings.theme is also "light"!
console.log(original.settings.theme); // "light" (Mutated unexpectedly!)
Deep Copy Solutions:
-
structuredClone()(Modern Standard): Built into modern browsers and Node.js (v17+). Handles circular references, Dates, Sets, Maps, and TypedArrays:const deepCopy = structuredClone(original);deepCopy.settings.theme = "sepia";console.log(original.settings.theme); // "dark" (Untouched!) -
JSON.parse(JSON.stringify(obj))(Legacy Workaround): Simple but has severe limitations: loses functions,undefined,Symbol,Infinity,NaN, and convertsDateobjects into ISO strings. -
lodash.cloneDeep: Common utility library function for older enterprise environments.
4. Immutable Operations in React
In React, state must NEVER be mutated directly.
Why Immutability is Mandatory
React components re-render when state changes. To detect if state changed, React performs a blazing-fast shallow reference equality check using Object.is(oldState, newState):
// If you mutate state directly:
state.todos.push(newTodo);
setState(state);
// React checks: Object.is(state, state) -> true!
// Result: React assumes NOTHING changed and skips re-rendering!
By creating a new object or array with an updated reference, React instantly recognizes that state has changed and triggers a DOM update.
The React Immutable Toolkit
| Task | Mutating (Avoid in React) | Immutable Best Practice |
|---|---|---|
| Add item to array | arr.push(item) | [...arr, item] or [item, ...arr] |
| Remove item from array | arr.splice(index, 1) | arr.filter(item => item.id !== targetId) |
| Update item in array | arr[index] = updated | arr.map(item => item.id === targetId ? { ...item, ...updates } : item) |
| Sort array | arr.sort() | [...arr].sort() or arr.toSorted() |
| Reverse array | arr.reverse() | [...arr].reverse() or arr.toReversed() |
| Update object property | obj.prop = value | { ...obj, prop: value } |
| Update nested object | obj.nested.prop = value | { ...obj, nested: { ...obj.nested, prop: value } } |
// Complex Nested State Update Example
const [userProfile, setUserProfile] = useState({
id: 101,
name: "Sarwvidya",
preferences: {
notifications: {
email: true,
push: false
}
}
});
// Update push notifications immutably:
setUserProfile(prev => ({
...prev,
preferences: {
...prev.preferences,
notifications: {
...prev.preferences.notifications,
push: true
}
}
}));
5. Summary Checklist
- Use
map()for transforming data into JSX components; remember to provide a uniquekey. - Use
filter()andreduce()for declarative filtering, aggregation, and data reshaping. - Know the distinction between
forEach()(side effects, returnsundefined) andmap()(pure transform, returns new array). - Beware of shallow copies (
{ ...obj }): nested references are shared. UsestructuredClone()for full deep cloning. - Always update React state immutably so that shallow reference checks (
Object.is) trigger re-renders properly.