Write a function that takes an object obj and returns a new immutable version of this object.
An immutable object is an object that can't be altered and will throw an error if any attempt is made to alter it.
There are three types of error messages that can be produced from this new object.
`Error Modifying: ${key}`.`Error Modifying Index: ${index}`.`Error Calling Method: ${methodName}`. You may assume the only methods that can mutate an array are ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse'].obj is a valid JSON object or array, meaning it is the output of JSON.parse().
Note that a string literal should be thrown, not an Error.
Example 1:
Input:
obj = {
"x": 5
}
fn = (obj) => {
obj.x = 5;
return obj.x;
}
Output: {"value": null, "error": "Error Modifying: x"}
Explanation: Attempting to modify a key on an object resuts in a thrown error. Note that it doesn't matter that the value was set to the same value as it was before.
Example 2:
Input:
obj = [1, 2, 3]
fn = (arr) => {
arr[1] = {};
return arr[2];
}
Output: {"value": null, "error": "Error Modifying Index: 1"}
Explanation: Attempting to modify an array results in a thrown error.
Example 3:
Input:
obj = {
"arr": [1, 2, 3]
}
fn = (obj) => {
obj.arr.push(4);
return 42;
}
Output: { "value": null, "error": "Error Calling Method: push"}
Explanation: Calling a method that can result in a mutation results in a thrown error.
Example 4:
Input:
obj = {
"x": 2,
"y": 2
}
fn = (obj) => {
return Object.keys(obj);
}
Output: {"value": ["x", "y"], "error": null}
Explanation: No mutations were attempted so the function returns as normal.
Constraints:
obj is a valid JSON object or array2 <= JSON.stringify(obj).length <= 105Problem Overview: The task is to make a JavaScript object completely immutable. After the transformation, neither the object nor any nested objects or arrays inside it should allow modifications such as property updates, additions, or deletions.
Approach 1: Shallow Freeze with Object.freeze() (O(n) time, O(1) space)
The simplest idea is calling Object.freeze(obj). This prevents modification of the object's top-level properties. Any attempt to assign new values, delete properties, or extend the object fails in strict mode. However, this approach is only shallow. Nested objects and arrays remain mutable because Object.freeze does not recursively traverse the structure. This method works only when the object contains primitives or when deep immutability is not required.
Approach 2: Recursive Deep Freeze (DFS Traversal) (O(n) time, O(h) space)
The correct solution walks through the object recursively and freezes every nested structure. Start by iterating over each property using Object.keys() or a for...in loop. When a value is itself an object or array, recursively apply the same freezing logic before freezing the current object. This forms a depth-first traversal of the object graph. Each object is visited once, giving O(n) time complexity where n is the total number of nested objects and properties. The recursion stack consumes O(h) space where h is the maximum nesting depth.
This approach guarantees deep immutability. Arrays, nested objects, and complex structures become fully locked after processing. The technique mirrors classic recursive traversal patterns used in recursion and tree-style object processing in JavaScript.
Approach 3: Iterative Deep Freeze with Stack (O(n) time, O(n) space)
An alternative avoids recursion by using an explicit stack or queue. Push the root object onto a stack, then repeatedly pop elements and freeze them. Before freezing, scan their properties and push any nested objects or arrays onto the stack. This produces the same behavior as DFS but avoids recursion depth limits. The tradeoff is additional auxiliary memory because the stack may temporarily hold many objects.
Recommended for interviews: The recursive deep freeze approach is the expected solution. It demonstrates understanding of object traversal, recursion, and how JavaScript immutability works internally with Object.freeze. Mentioning the shallow-freeze limitation shows conceptual clarity, while implementing the recursive version shows practical skill working with nested structures and object manipulation.
TypeScript
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Shallow Freeze using Object.freeze | O(n) | O(1) | When the object has no nested objects or deep immutability is unnecessary |
| Recursive Deep Freeze (DFS) | O(n) | O(h) | Best general solution for deeply nested objects and arrays |
| Iterative Deep Freeze using Stack | O(n) | O(n) | Useful when recursion depth could exceed call stack limits |
Don't Make This Coding Interview Mistake! | Jewels and Stones - Leetcode 771 • Greg Hogg • 443,129 views views
Watch 9 more video solutions →Practice Make Object Immutable with our built-in code editor and test cases.
Practice on FleetCodePractice this problem
Open in Editor