跳至主要內容

2691. 不可变辅助工具 🔒


2691. 不可变辅助工具 🔒open in new window

🔴   🔗 力扣open in new window LeetCodeopen in new window

题目

Creating clones of immutable objects with minor alterations can be a tedious process. Write a class ImmutableHelper that serves as a tool to help with this requirement. The constructor accepts an immutable object obj which will be a JSON object or array.

The class has a single method produce which accepts a function mutator. The function returns a new object which is similar to the original except it has those mutations applied.

mutator accepts a proxied version of obj. A user of this function can (appear to) mutate this object, but the original object obj should not actually be effected.

For example, a user could write code like this:

const originalObj = { x: 5 };
const helper = new ImmutableHelper(originalObj);
const newObj = helper.produce((proxy) => {
	proxy.x = proxy.x + 1;
});
console.log(originalObj); // {"x": 5}
console.log(newObj); // {"x": 6}

Properties of the mutator function:

  • It will always return undefined.
  • It will never access keys that don't exist.
  • It will never delete keys (delete obj.key)
  • It will never call methods on a proxied object (push, shift, etc).
  • It will never set keys to objects (proxy.x = {})

Note on how the solution will be tested: the solution validator will only analyze differences between what was returned and the original obj. Doing a full comparison would be too computationally expensive. Also, any mutations to the original object will result in a wrong answer.

Example 1:

Input:

obj = {"val": 10},

mutators = [
  proxy => { proxy.val += 1; },
  proxy => { proxy.val -= 1; }
]

Output:

[{"val": 11}, {"val": 9}]

Explanation:

const helper = new ImmutableHelper({ val: 10 });
helper.produce((proxy) => {
	proxy.val += 1;
}); // { "val": 11 }
helper.produce((proxy) => {
	proxy.val -= 1;
}); // { "val": 9 }

Example 2:

Input:

obj = {"arr": [1, 2, 3]}
mutators = [
  proxy => {
    proxy.arr[0] = 5;
    proxy.newVal = proxy.arr[0] + proxy.arr[1];
  }
]

Output:

[{"arr": [5, 2, 3], "newVal": 7 }]

Explanation: Two edits were made to the original array. The first element in the array was to set 5. Then a new key was added with a value of 7.

Example 3:

Input:

obj = {"obj": {"val": {"x": 10, "y": 20}}}

mutators = [
  proxy => {
    let data = proxy.obj.val;
    let temp = data.x;
    data.x = data.y;
    data.y = temp;
  }
]

Output:

[{"obj": {"val": {"x": 20, "y": 10}}}]

Explanation: The values of "x" and "y" were swapped.

Constraints:

  • 2 <= JSON.stringify(obj).length <= 4 * 10^5
  • mutators is an array of functions
  • total calls to produce() < 10^5

题目大意

创建带有微小修改的不可变对象的克隆副本是一个繁琐的过程。请你编写一个名为 ImmutableHelper 的类,作为满足这一要求的工具。构造函数接受一个不可变对象 obj ,该对象将是一个 JSON 对象或数组。

该类有一个名为 produce 的方法,它接受一个名为 mutator 的函数。该函数返回一个新的对象,它与原始对象相似,但应用了这些变化。

mutator 函数接受 obj代理 版本。函数的使用者可以(看起来)对该对象进行修改,但原始对象 obj 实际上没有被改变。

const originalObj = { x: 5 };
const helper = new ImmutableHelper(originalObj);
const newObj = helper.produce((proxy) => {
	proxy.x = proxy.x + 1;
});
console.log(originalObj); // {"x": 5}
console.log(newObj); // {"x": 6}

例如,用户可以编写如下代码:

mutator 函数的属性:

  • 它始终返回 undefined
  • 它永远不会访问不存在的键。
  • 它永远不会删除键( delete obj.key )。
  • 它永远不会在代理对象上调用方法( pushshift 等)。
  • 它永远不会将键设置为对象( proxy.x = {} )。

关于如何测试解决方案的说明: 解决方案验证器仅分析返回结果与原始 obj 之间的差异。进行完全比较的计算开销太大。此外,对原始对象进行的任何变更都将导致答案错误。

提示:

  • 2 <= JSON.stringify(obj).length <= 4 * 10^5
  • produce() 的总调用次数 < 10^5

解题思路

可以使用 Proxy 和深拷贝机制,确保原始对象不被改变的同时,允许用户通过代理对象“修改”属性。

  1. 深拷贝原始对象:在构造函数中,通过 JSON.parse(JSON.stringify(obj)) 进行深拷贝,以确保 this.original 是一个全新的对象。

  2. 使用 Proxy:在 produce 方法中,创建一个 Proxy,用于代理对原始对象的访问,代理允许拦截对对象属性的设置操作。

  3. Mutator 函数:调用传入的 mutator 函数,并将代理对象作为参数传递给它。用户可以通过这个代理对象进行修改。

  4. 返回新对象:在 produce 方法结束时,再进行一次深拷贝以创建一个新的对象并返回。

代码

class ImmutableHelper {
	constructor(obj) {
		this.original = JSON.parse(JSON.stringify(obj)); // 深拷贝原始对象
	}

	produce(mutator) {
		// 使用 Proxy 来修改对象
		const proxy = new Proxy(this.original, {
			set: (target, key, value) => {
				// 只允许修改代理,不允许直接修改原始对象
				if (key in target) {
					target[key] = value; // 代理修改
					return true;
				}
				throw new Error(`Cannot set property ${key} on proxy`);
			}
		});

		// 调用 mutator 函数
		mutator(proxy);

		// 深拷贝代理对象,返回一个新对象,应用修改
		return JSON.parse(JSON.stringify(proxy));
	}
}

相关题目

题号标题题解标签难度
2690无穷方法对象 🔒open in new window[✓]
2692使对象不可变 🔒open in new window[✓]