We use cookies to enhance your experience on the site
CodeWorlds

Immutable Data Patterns

In the laboratories of Jurassic Park, a strict rule applies - you never modify the original DNA sample. Every change creates a new copy. We apply the same approach in functional programming with data - original structures remain untouched, and every modification creates a new object.

Spread Operator - Copying and Extending

The spread operator (

...
) is the fundamental tool for creating immutable copies of objects and arrays:

1// Copying an object with modification
2const dinosaur = { name: 'Rex', health: 100, zone: 'A' };
3const movedDino = { ...dinosaur, zone: 'B' };
4// dinosaur: { name: 'Rex', health: 100, zone: 'A' } - unchanged
5// movedDino: { name: 'Rex', health: 100, zone: 'B' } - new object
6
7// Copying an array with added element
8const zones = ['A', 'B', 'C'];
9const extendedZones = [...zones, 'D'];
10// zones: ['A', 'B', 'C'] - unchanged
11// extendedZones: ['A', 'B', 'C', 'D']
12
13// Removing an element from an array (without mutation)
14const dinosaurs = ['Rex', 'Blue', 'Brachio', 'Stego'];
15const withoutBlue = dinosaurs.filter((d) => d !== 'Blue');
16// dinosaurs: ['Rex', 'Blue', 'Brachio', 'Stego'] - unchanged
17// withoutBlue: ['Rex', 'Brachio', 'Stego']

Nested Objects - Deep Immutability

The spread operator creates a shallow copy - nested objects are still shared! You must spread at every level:

1const park = {
2  name: 'Jurassic Park',
3  zones: {
4    A: { dinosaurs: ['Rex'], voltage: 10000 },
5    B: { dinosaurs: ['Brachio'], voltage: 5000 },
6  },
7};
8
9// WRONG - shallow copy, zones is shared
10const badCopy = { ...park };
11badCopy.zones.A.voltage = 0; // ALSO CHANGES the original!
12
13// CORRECT - deep copy at every level
14const updatedPark = {
15  ...park,
16  zones: {
17    ...park.zones,
18    A: {
19      ...park.zones.A,
20      voltage: 15000,
21    },
22  },
23};
24// park.zones.A.voltage: 10000 (unchanged)
25// updatedPark.zones.A.voltage: 15000 (new object)

Object.freeze() - Freezing Objects

Object.freeze()
prevents modification of an object at runtime. An attempt to change is silently ignored (or throws an error in strict mode):

1const CONFIG = Object.freeze({
2  maxDinosaurs: 100,
3  maxVoltage: 50000,
4  alertThreshold: 3,
5});
6
7CONFIG.maxDinosaurs = 200; // Ignored! (or Error in strict mode)
8console.log(CONFIG.maxDinosaurs); // still 100
9
10// WARNING: freeze is shallow!
11const deepConfig = Object.freeze({
12  zones: { A: { limit: 10 } },
13});
14deepConfig.zones.A.limit = 999; // THIS WILL WORK - freeze is shallow

structuredClone() - Deep Copying

structuredClone()
creates a deep copy of an object - all nested structures are independent:

1const original = {
2  name: 'Jurassic Park',
3  zones: [
4    { id: 'A', dinosaurs: [{ name: 'Rex', health: 100 }] },
5    { id: 'B', dinosaurs: [{ name: 'Blue', health: 95 }] },
6  ],
7};
8
9const deepCopy = structuredClone(original);
10deepCopy.zones[0].dinosaurs[0].health = 50;
11
12console.log(original.zones[0].dinosaurs[0].health); // 100 (unchanged!)
13console.log(deepCopy.zones[0].dinosaurs[0].health);  // 50 (independent copy)

Immutable Array Updates

A complete set of array operations without mutation:

1const dinosaurs = [
2  { id: 1, name: 'Rex', health: 100 },
3  { id: 2, name: 'Blue', health: 95 },
4  { id: 3, name: 'Brachio', health: 88 },
5];
6
7// Adding
8const added = [...dinosaurs, { id: 4, name: 'Stego', health: 100 }];
9
10// Removing
11const removed = dinosaurs.filter((d) => d.id !== 2);
12
13// Updating
14const updated = dinosaurs.map((d) =>
15  d.id === 1 ? { ...d, health: 80 } : d
16);
17
18// Sorting (without mutation - create a copy before sort)
19const sorted = [...dinosaurs].sort((a, b) => a.health - b.health);
Go to CodeWorlds