const data = {
key: ['value1', 'value2'],
find: {
key: 'key',
key2: [{
key12: 'value12',
key45: 'value 45',
value: 'some value'
}, {
key32: 'value122',
key33: 'value 435',
value: 'some value'
}, {
key56: 'value56',
key51: 'value 5111',
value: 'some value'
}]
},
key22: ['some', 'test', 'value'],
};
value
const newData = {...data, find: {
...data.find,
key2: {
...data.find.key2.map(obj => {
return {...obj, value: 'new value'}
})
}
}}
value
, которое нужно найти и заменить. Как можно решить эту задачу при текущем условии? const newData = JSON.parse(
JSON.stringify(data),
(key, value) => key === 'value' ? 'new value' : value
);
function clone(obj) {
if (obj === null || typeof(obj) !== 'object' || 'isActive' in obj) return obj;
const temp = obj.constructor();
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
obj['isActive'] = null;
temp[key] = key === 'value' ? 'new value' : clone(obj[key]);
delete obj['isActive'];
}
}
return temp;
}
clone(data);
function cloneWithReplace(
// клонируемый объект
obj,
// объект с функциями вида value => newValue, в соответствующих ключах для замены
// нужен не всегда, поэтому по умолчанию сделаем пустой объект
replacers = {},
// Map с значениями, которые уже склонировали, дабы не попасть в рекурсию
// так как внешний код обычно его не будет передавать, зададим значение по умолчанию
storeMap = new Map()
) {
// для начала чекнем, что объект уже клонировали:
if(storeMap.has(obj)) {
return storeMap.get(obj);
}
// получим тип объекта, он нам пригодится пару раз, дабы отличать функции
const type = typeof obj;
// если obj примитив, то его можно просто вернуть
if(obj === null || (type !== 'object' && type !== 'function')) {
return obj;
}
// создадим переменную с результатом и инициируем ее в зависимости от типа оригинала
let result;
if(type === 'function') {
// функцию можно "склонировать" лишь обернув
result = function() {
return obj.apply(this, arguments);
};
// неплохо бы, чтоб клон правильно сообщал имя функции и количество аргументов
// но так как IE не ест такую магию, обернем в try-catch
try {
Object.defineProperties(result, {
name: Object.getOwnPropertyDescriptor(obj, 'name'),
length: Object.getOwnPropertyDescriptor(obj, 'length')
});
} catch {}
} else if(Array.isArray(obj)) {
// массивы клонируем рекурсивно, при помощи map
result = obj.map(value => cloneWithReplace(value, replacers, storeMap));
// так как нормальные массивы не содержат других полей, кроме числовых
// можно сохранить клон в защиту от рекурсии и вернуть результат
storeMap.set(obj, result);
return result;
} else {
// для всех других объектов просто создадим новый объект и скопируем ему ссылку на прототип
result = Object.setPrototypeOf({}, Object.getPrototypeOf(obj));
}
// сохраним клон в защиту от рекурсии
storeMap.set(obj, result);
// осталось склонировать поля с заменой тех случаев, где у нас есть replacer
for(const key of Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))) {
if(typeof replacers[key] === 'function') {
// если есть replacer используем его
result[key] = replacers[key](obj[key]);
} else {
// иначе клонируем поле рекурсивно
result[key] = cloneWithReplace(obj[key], replacers, storeMap);
}
}
return result;
}
// используем так:
const newData = cloneWithReplace(data, {
value: () => 'new value'
});