In this chapter, we learn about two different ways of updating data:
The latter way is similar to first making a copy and then changing it destructively, but it does both at the same time.
The following code shows a function that updates object properties destructively and uses it on an object.
function setPropertyDestructively(obj, key, value) {
obj[key] = value;
return obj;
}
const obj = {city: 'Berlin', country: 'Germany'};
setPropertyDestructively(obj, 'city', 'Munich');
assert.deepEqual(obj, {city: 'Munich', country: 'Germany'});
The following code demonstrates non-destructive updating of an object:
function setPropertyNonDestructively(obj, key, value) {
const updatedObj = {};
for (const [k, v] of Object.entries(obj)) {
updatedObj[k] = (k === key ? value : v);
}
return updatedObj;
}
const obj = {city: 'Berlin', country: 'Germany'};
const updatedObj = setPropertyNonDestructively(obj, 'city', 'Munich');
// We have created an updated object:
assert.deepEqual(updatedObj, {city: 'Munich', country: 'Germany'});
// But we didn’t change the original:
assert.deepEqual(obj, {city: 'Berlin', country: 'Germany'});
Spreading makes setPropertyNonDestructively()
more concise:
Both versions of setPropertyNonDestructively()
update shallowly: They only change the top level of an object.
The following code shows a function that updates Array elements destructively and uses it on an Array.
function setElementDestructively(arr, index, value) {
arr[index] = value;
}
const arr = ['a', 'b', 'c', 'd', 'e'];
setElementDestructively(arr, 2, 'x');
assert.deepEqual(arr, ['a', 'b', 'x', 'd', 'e']);
The following code demonstrates non-destructive updating of an Array:
function setElementNonDestructively(arr, index, value) {
const updatedArr = [];
for (const [i, v] of arr.entries()) {
updatedArr.push(i === index ? value : v);
}
return updatedArr;
}
const arr = ['a', 'b', 'c', 'd', 'e'];
const updatedArr = setElementNonDestructively(arr, 2, 'x');
assert.deepEqual(updatedArr, ['a', 'b', 'x', 'd', 'e']);
assert.deepEqual(arr, ['a', 'b', 'c', 'd', 'e']);
.slice()
and spreading make setElementNonDestructively()
more concise:
function setElementNonDestructively(arr, index, value) {
return [
...arr.slice(0, index), value, ...arr.slice(index+1)];
}
Both versions of setElementNonDestructively()
update shallowly: They only change the top level of an Array.
So far, we have only updated data shallowly. Let’s tackle deep updating. The following code shows how to do it manually. We are changing name and employer.
const original = {name: 'Jane', work: {employer: 'Acme'}};
const updatedOriginal = {
...original,
name: 'John',
work: {
...original.work,
employer: 'Spectre'
},
};
assert.deepEqual(
original, {name: 'Jane', work: {employer: 'Acme'}});
assert.deepEqual(
updatedOriginal, {name: 'John', work: {employer: 'Spectre'}});
The following function implements generic deep updating.
function deepUpdate(original, keys, value) {
if (keys.length === 0) {
return value;
}
const currentKey = keys[0];
if (Array.isArray(original)) {
return original.map(
(v, index) => index === currentKey
? deepUpdate(v, keys.slice(1), value) // (A)
: v); // (B)
} else if (typeof original === 'object' && original !== null) {
return Object.fromEntries(
Object.entries(original).map(
(keyValuePair) => {
const [k,v] = keyValuePair;
if (k === currentKey) {
return [k, deepUpdate(v, keys.slice(1), value)]; // (C)
} else {
return keyValuePair; // (D)
}
}));
} else {
// Primitive value
return original;
}
}
If we see value
as the root of a tree that we are updating, then deepUpdate()
only deeply changes a single branch (line A and C). All other branches are copied shallowly (line B and D).
This is what using deepUpdate()
looks like: