我正在开发一个 TypeScript 项目,我需要比较两个具有潜在嵌套结构的对象,并识别它们之间已更改的字段。例如,考虑一个旧对象 oldObject,其中包含姓名、年龄、城市、朋友(数组)等字段,以及其中可能嵌套的对象。现在,我有一个具有相同结构的新对象 newObject,但某些字段的值可能会更新。
这是一个例子:
const oldObject = { name: 'John', age: 30, city: 'New York', friends: ['ali', 'qasim'], def: { 'a': 1, 'b': 2 } };
const newObject = { name: 'John', age: 35, city: 'New York', friends: ['ali', 'haider'] };
在这种情况下,我需要能够识别并提取 oldObject 和 newObject 之间更改的字段及其新值,同时保留嵌套结构。
我尝试了比较函数,但它无法正确处理嵌套结构和数组。如何在 TypeScript 中实现一个解决方案,准确识别更改的字段,同时保留对象的嵌套结构?
解决 OP 问题的可行方法可能是将任何给定的数据结构序列化为基于键路径的映射/索引。
Map
的序列化变体。
Set
实例,该实例仅保存每个相关的序列化数据结构的键路径字符串。
通过使用新的
Set
方法 intersection
和 difference
(利用后两者的 js 核心实现),可以检索特定的键路径字符串集,例如重叠的键路径和唯一的键路径每个数据结构。
达到这一点后,从所有创建的地图和集合中创建一个
diff
对象非常容易,这是......
计算一个
changed
差异条目,指出一个相同的键路径上的不同值。
创建一个
deleted
差异条目,其特征是基于键路径的键值对,这些键值对对于所提供的最近的数据对象是唯一的。
创建一个
added
差异条目,其特征是基于键路径的键值对,这些键值对对于所提供的当前数据对象是唯一的。
const oldObject = {
name: 'John', age: 30,
friends: ['ali', 'qasim', { foo: 'Foo', 'foo bar': { baz: 'Baz', biz: 'Biz' } }],
};
const newObject = {
name: 'John', age: 35, city: 'New York',
friends: ['ali', 'haider', { 'foo bar': { baz: 'BAZ'}, foo: 'Foo' }],
};
const diff = diffDataValuesByKeypath(oldObject, newObject);
console.log({ oldObject, newObject, diff });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/3.36.0/minified.js"></script>
<script>
function concatKeypath(keypath, key) {
// - test for a key which can be used with dot chaining.
// - see ... [https://regex101.com/r/tew8gr/1]
return (/^[_$\p{L}][_$\p{L}\p{N}]*$/u).test(key)
// - concat a dot chained key.
? (!!keypath && `${ keypath }.${ key }` || key)
// - concat a quoted key with square bracket notation.
: `${ keypath }["${ key }"]`;
}
function mapDataStructure(value, keypath = '', map = new Map) {
if (Array.isArray(value)) {
value
.forEach((item, idx) =>
mapDataStructure(item, `${ keypath }[${ idx }]`, map)
);
} else if (value && typeof value === 'object') {
Object
.entries(value)
.forEach(([key, val]) =>
mapDataStructure(val, concatKeypath(keypath, key), map)
);
} else {
map.set(keypath, value);
}
return map;
}
function diffDataValuesByKeypath(recentData, currentData) {
const recentMap = mapDataStructure(recentData);
const currentMap = mapDataStructure(currentData);
const recentPaths = new Set([...recentMap.keys()]);
const currentPaths = new Set([...currentMap.keys()]);
const collidingPaths = recentPaths.intersection(currentPaths);
const uniqueRecentPaths = recentPaths.difference(currentPaths);
const uniqueCurrentPaths = currentPaths.difference(recentPaths);
return {
changed: [...collidingPaths.values()]
.reduce((result, keypath) => {
const recentValue = recentMap.get(keypath);
const currentValue = currentMap.get(keypath);
if (!Object.is(recentValue, currentValue)) {
result[keypath] = {
recent: recentValue,
current: currentValue,
};
}
return result;
}, {}),
deleted: [...uniqueRecentPaths.values()]
.reduce((result, keypath) => {
result[keypath] = recentMap.get(keypath);
return result;
}, {}),
added: [...uniqueCurrentPaths.values()]
.reduce((result, keypath) => {
result[keypath] = currentMap.get(keypath);
return result;
}, {}),
};
}
</script>
const oldObject = { name: 'John', age: 30, city: 'New York', friends: ['ali', 'qasim'], def: { 'a': 1, 'b': 2 } };
const newObject = { name: 'John', age: 35, city: 'New York', friends: ['ali', 'haider'] };
let oldobj = Object.keys(oldObject)
console.log(oldobj)
let newobj = Object.keys(newObject)
console.log(newobj)
let result = oldobj.filter(x => !newobj.includes(x));
if(result.length> 0){
console.log(oldObject.def)
}
// 在 2 个数组中查找键。并检查两个区域中的密钥是否相同
要准确识别和比较具有嵌套结构的 TypeScript 对象中更改的字段,您可以使用递归方法。以下是如何实现一个函数来实现此目的:
function findChangedFields(oldObj: any, newObj: any): { old: any, new: any } {
const changedFields: { old: any, new: any } = { old: {}, new: {} };
for (const key in oldObj) {
if (oldObj.hasOwnProperty(key)) {
if (!newObj.hasOwnProperty(key)) {
changedFields.old[key] = oldObj[key];
changedFields.new[key] = undefined; // Field does not exist in new object
} else if (Array.isArray(oldObj[key]) && Array.isArray(newObj[key])) {
if (JSON.stringify(oldObj[key]) !== JSON.stringify(newObj[key])) {
changedFields.old[key] = oldObj[key];
changedFields.new[key] = newObj[key];
}
} else if (typeof oldObj[key] === 'object' && typeof newObj[key] === 'object') {
const nestedChanges = findChangedFields(oldObj[key], newObj[key]);
if (Object.keys(nestedChanges.old).length > 0 || Object.keys(nestedChanges.new).length > 0) {
changedFields.old[key] = nestedChanges.old;
changedFields.new[key] = nestedChanges.new;
}
} else {
if (oldObj[key] !== newObj[key]) {
changedFields.old[key] = oldObj[key];
changedFields.new[key] = newObj[key];
}
}
}
}
// Check for new keys in newObj
for (const key in newObj) {
if (newObj.hasOwnProperty(key) && !oldObj.hasOwnProperty(key)) {
changedFields.old[key] = undefined; // Field does not exist in old object
changedFields.new[key] = newObj[key];
}
}
return changedFields;
}
// Example usage:
const oldObject = { name: 'John', age: 30, city: 'New York', friends: ['ali', 'qasim'], def: { 'a': 1, 'b': 3 }, ng: { a: { d: 1 } } };
const newObject = { name: 'John', age: 35, city: 'New York', friends: ['ali', 'haider'], def: {}, ng: { a: {} } };
const changedFields = findChangedFields(oldObject, newObject);
console.log(changedFields);
该函数递归遍历对象和数组来比较它们的元素。如果发现差异,它将在changedFields对象中记录更改的字段及其新值。
您可以使用示例对象 oldObject 和 newObject 测试此函数,以查看更改的字段及其新值。