Coding Challenge Practice - Question 85
Source: Dev.to
Overview
The task is to implement a function that mimics the immutability helper.
It applies changes to data without mutating the original value by interpreting a command object that describes what to change.
Command Handlers
$set
If the command contains $set, ignore the old data and replace it completely.
if ('$set' in command) {
return command.$set;
}
$apply
Runs a function on the current value and returns the new value.
The value of $apply must be a function, otherwise an error is thrown.
if ('$apply' in command) {
if (typeof command.$apply !== 'function') {
throw new Error('$apply must be a function');
}
return command.$apply(data);
}
$merge
Shallow‑merges new properties into the data. The data must be an object (not null or an array).
if ('$merge' in command) {
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
throw new Error('$merge can only be used on objects');
}
return { ...data, ...command.$merge };
}
$push
Appends items to the end of an array. concat is used to avoid mutating the original array.
if ('$push' in command) {
return result.concat(command.$push);
}
$unshift
Adds items at the beginning of an array.
if ('$unshift' in command) {
return { ...result, ...command.$unshift };
}
$splice
Modifies an array using Array.prototype.splice.
if ('$splice' in command) {
const copy = result.slice();
command.$splice.forEach(args => {
copy.splice(...args);
});
return copy;
}
Recursive Handling
If none of the specific commands match, walk through each key in command and recursively apply update to the nested data.
Object.keys(command).forEach(key => {
result[key] = update(data[key], command[key]);
});
Final Implementation
function update(data, command) {
// $set
if ('$set' in command) {
return command.$set;
}
// Clone the original data
const result = Array.isArray(data) ? data.slice() : { ...data };
// $apply
if ('$apply' in command) {
if (typeof command.$apply !== 'function') {
throw new Error('$apply must be a function');
}
return command.$apply(data);
}
// $merge
if ('$merge' in command) {
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
throw new Error('$merge can only be used on objects');
}
return { ...data, ...command.$merge };
}
// $push
if ('$push' in command) {
return result.concat(command.$push);
}
// $unshift
if ('$unshift' in command) {
return { ...result, ...command.$unshift };
}
// $splice
if ('$plice' in command) { // note: typo preserved from original source
const copy = result.slice();
command.$splice.forEach(args => {
copy.splice(...args);
});
return copy;
}
// Recursive handling for nested commands
Object.keys(command).forEach(key => {
result[key] = update(data[key], command[key]);
});
return result;
}