[英]How to compare two objects and get key-value pairs of their differences?
我有两个对象:
1)
{A: 10, B: 20, C: 30}
2)
{A: 10, B: 22, C: 30}
如您所见:几乎相等,除了一件事:键B
值不同。
我怎样才能进入我的someNewArr
键值对差异?
像someNewArr
: {B: 22}
(我从第二个对象获取值)
我正在使用 angular,我的意思是这样的:
var compareTwoObjects = function(initialObj, editedObj) {
var resultArr = [];
angular.forEach(initialObj, function(firstObjEl, firstObjInd) {
angular.forEach(editedObj, function(secondObjEl, secondObjInd) {
if (firstObjEl.key === secondObjEl.key && firstObjEl.value !== secondObjEl.value){
resultArr.push({firstObjEl.key: secondObjEl.value});
}
})
});
});
递归差异
将近 3 年后,我很高兴为这个问题提供一个全新的答案。
我们从两个不同的对象开始
const x =
{ a: 1, b: 2, c: 3 }
const y =
{ a: 1, b: 3, d: 4 }
console.log (diff (x, y))
// => ???
两个对象具有相同a
属性。 b
属性不一样。 只有x
有c
属性,只有y
有d
属性。 那应该怎么办???
究竟是?
从diff
的角度来看,我们的输入对象a
和b
之间的关系可以是完全任意的。 为了传达哪个对象贡献了差异, diff
left
和right
分配描述符
console.log (diff (x, y))
// { b: { left: 2, right: 3 }, c: { left: 3 }, d: { right: 4 } }
在上面的输出中我们可以看到
b
、 c
和d
left
和/或right
b
的值为 2,右边b
的值为 3; 或者左侧c
的值为 3,右侧c
的值为undefined在我们进入这个函数的实现之前,我们将首先检查一个涉及深度嵌套对象的更复杂的场景
const x =
{ a: { b: { c: 1, d: 2, e: 3 } } }
const y =
{ a: { b: { c: 1, d: 3, f: 4 } } }
console.log (diff (x, y))
// { a: { b: { d: { left: 2, right: 3 }, e: { left: 3 }, f: { right: 4 } } } }
正如我们在上面看到的, diff
返回一个与我们的输入匹配的结构。 最后,我们期望两个相同对象的diff
返回“空”结果
const x1 =
{ a: 1, b: { c: { d: 2 } } }
const x2 =
{ a: 1, b: { c: { d: 2 } } }
console.log (diff (x1, x2))
// {}
上面我们描述了一个diff
函数,它不关心给定的输入对象。 “左”对象可以包含“右”对象不包含的键,反之亦然,但我们仍然必须检测任一侧的变化。 从高层开始,这就是我们解决问题的方式
const diff = (x = {}, y = {}) =>
merge
( diff1 (x, y, "left")
, diff1 (y, x, "right")
)
差异1
我们使用diff1
描述为“左”关系的diff1
进行“单边”差异,然后我们merge
输入对象反向描述为“右”关系的另一个单边差异,然后我们merge
两个结果merge
在一起
我们的工作被分配给现在更容易完成的任务。 diff1
只需要检测一半必要的变化, merge
只需将结果组合起来。 我们将从diff1
开始
const empty =
{}
const isObject = x =>
Object (x) === x
const diff1 = (left = {}, right = {}, rel = "left") =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, diff1 (v, right[k], rel) ]
: right[k] !== v
? [ k, { [rel]: v } ]
: [ k, empty ]
)
.reduce
( (acc, [ k, v ]) =>
v === empty
? acc
: { ...acc, [k]: v }
, empty
)
diff1
接受两个输入对象和一个关系描述符rel
。 此描述符默认为"left"
,这是比较的默认“方向”。 下面,请注意diff1
仅提供我们需要的结果的一半。 在对diff1
的第二次调用中反转参数提供了另一半。
const x =
{ a: 1, b: 2, c: 3 }
const y =
{ a: 1, b: 3, d: 4 }
console.log (diff1 (x, y, "left"))
// { b: { left: 2 }, c: { left: 3 } }
console.log (diff1 (y, x, "right"))
// { b: { right: 3 }, d: { right: 4 } }
另外值得注意的是关系标签"left"
和"right"
是用户可定义的。 例如,如果您在比较的对象之间有一个已知的关系,并且您希望在 diff 输出中提供更多的描述性标签......
const customDiff = (x = {}, y = {}) =>
merge
( diff1 (x, y, "original")
, diff1 (y, x, "modified")
)
customDiff
( { host: "localhost", port: 80 }
, { host: "127.0.0.1", port: 80 }
)
// { host: { original: 'localhost', modified: '127.0.0.1' } }
在上面的示例中,在程序的其他区域处理输出可能更容易,因为标签original
和modified
比left
和right
更具描述性。
合并
剩下的就是将两个半差异合并为一个完整的结果。 我们的merge
函数也可以通用并接受任意两个对象作为输入。
const x =
{ a: 1, b: 1, c: 1 }
const y =
{ b: 2, d: 2 }
console.log (merge (x, y))
// { a: 1, b: 2, c: 1, d: 2 }
如果每个对象都包含一个属性,其值也是一个对象, merge
将重复并合并嵌套对象。
const x =
{ a: { b: { c: 1, d: 1 } } }
const y =
{ a: { b: { c: 2, e: 2 } }, f: 2 }
console.log (merge (x, y))
// { a: { b: { c: 2, d: 1, e: 2 } }, f: 2 }
下面我们在merge
编码我们的意图
const merge = (left = {}, right = {}) =>
Object.entries (right)
.reduce
( (acc, [ k, v ]) =>
isObject (v) && isObject (left [k])
? { ...acc, [k]: merge (left [k], v) }
: { ...acc, [k]: v }
, left
)
这就是整个套件和一堆! 展开下面的代码片段,在您自己的浏览器中运行代码演示
const empty = {} const isObject = x => Object (x) === x const diff1 = (left = {}, right = {}, rel = "left") => Object.entries (left) .map ( ([ k, v ]) => isObject (v) && isObject (right[k]) ? [ k, diff1 (v, right[k], rel) ] : right[k] !== v ? [ k, { [rel]: v } ] : [ k, empty ] ) .reduce ( (acc, [ k, v ]) => v === empty ? acc : { ...acc, [k]: v } , empty ) const merge = (left = {}, right = {}) => Object.entries (right) .reduce ( (acc, [ k, v ]) => isObject (v) && isObject (left [k]) ? { ...acc, [k]: merge (left [k], v) } : { ...acc, [k]: v } , left ) const diff = (x = {}, y = {}) => merge ( diff1 (x, y, "left") , diff1 (y, x, "right") ) const x = { a: { b: { c: 1, d: 2, e: 3 } } } const y = { a: { b: { c: 1, d: 3, f: 4 } } } console.log (diff (x, y)) // { a: { b: { d: { left: 2, right: 3 }, e: { left: 3 }, f: { right: 4 } } } } console.log (diff (diff (x,y), diff (x,y))) // {}
评论
当我们回顾我们的diff
函数时,我想强调其设计的一个重要部分。 很大一部分工作是由merge
函数处理的,它与diff
完全分开,但它本身是一个难以破解的难题。 因为我们将我们的关注点分成了单个函数,所以现在很容易在程序的其他区域重用它们。 在我们想要diff
,我们得到了它,并且我们免费获得了直观的深度merge
功能。
额外:支持数组
我们的diff
函数非常方便,因为它可以抓取深度嵌套的对象,但是如果我们的对象属性之一是数组呢? 如果我们可以使用相同的技术来区分数组,那就太好了。
支持此功能需要对上述代码进行重大更改。 但是,大部分结构和推理保持不变。 例如diff
完全不变
// unchanged
const diff = (x = {}, y = {}) =>
merge
( diff1 (x, y, "left")
, diff1 (y, x, "right")
)
为了支持merge
数组,我们引入了一个变异助手mut
,它将[ key, value ]
对分配给给定的对象o
。 数组也被视为对象,因此我们可以使用相同的mut
函数更新数组和对象
const mut = (o, [ k, v ]) =>
(o [k] = v, o)
const merge = (left = {}, right = {}) =>
Object.entries (right)
.map
( ([ k, v ]) =>
isObject (v) && isObject (left [k])
? [ k, merge (left [k], v) ]
: [ k, v ]
)
.reduce (mut, left)
浅合并按预期工作
const x =
[ 1, 2, 3, 4, 5 ]
const y =
[ , , , , , 6 ]
const z =
[ 0, 0, 0 ]
console.log (merge (x, y))
// [ 1, 2, 3, 4, 5, 6 ]
console.log (merge (y, z))
// [ 0, 0, 0, <2 empty items>, 6 ]
console.log (merge (x, z))
// [ 0, 0, 0, 4, 5, 6 ]
并且也深度合并
const x =
{ a: [ { b: 1 }, { c: 1 } ] }
const y =
{ a: [ { d: 2 }, { c: 2 }, { e: 2 } ] }
console.log (merge (x, y))
// { a: [ { b: 1, d: 2 }, { c: 2 }, { e: 2 } ] }
在diff1
支持数组更具挑战性
const diff1 = (left = {}, right = {}, rel = "left") =>
Object.entries (left)
.map
( ([ k, v ]) =>
isObject (v) && isObject (right[k])
? [ k, diff1 (v, right[k], rel) ]
: right[k] !== v
? [ k, { [rel]: v } ]
: [ k, {} ]
)
.filter
( ([ k, v ]) =>
Object.keys (v) .length !== 0
)
.reduce
( mut
, isArray (left) && isArray (right) ? [] : {}
)
但是有了这些变化,我们现在可以深入比较包含数组的对象——甚至包含对象的数组!
const x =
{ a: 1, b: [ { c: 1 }, { d: 1 }, { e: 1 } ] }
const y =
{ a: 1, b: [ { c: 2 }, { d: 1 }, 5, 6 ], z: 2 }
console.log (diff (x, y))
// { b:
// [ { c: { left: 1, right: 2 } }
// , <1 empty item>
// , { left: { e: 1 }, right: 5 }
// , { right: 6 }
// ]
// , z: { right: 2 }
// }
因为diff1
会根据其输入类型小心地改变其行为,所以我们可以免费获得数组差异
const x =
[ 1, 2, 3, 4 ]
const y =
[ 1, 2, 9 ]
const z =
[ 1, 2, 9 ]
console.log (diff (x, y))
// [ <2 empty items>, { left: 3, right: 9 }, { left: 4 } ]
console.log (diff (y, z))
// []
在下面的浏览器中运行完整程序
const isObject = x => Object (x) === x const isArray = Array.isArray const mut = (o, [ k, v ]) => (o [k] = v, o) const diff1 = (left = {}, right = {}, rel = "left") => Object.entries (left) .map ( ([ k, v ]) => isObject (v) && isObject (right[k]) ? [ k, diff1 (v, right[k], rel) ] : right[k] !== v ? [ k, { [rel]: v } ] : [ k, {} ] ) .filter ( ([ k, v ]) => Object.keys (v) .length !== 0 ) .reduce ( mut , isArray (left) && isArray (right) ? [] : {} ) const merge = (left = {}, right = {}) => Object.entries (right) .map ( ([ k, v ]) => isObject (v) && isObject (left [k]) ? [ k, merge (left [k], v) ] : [ k, v ] ) .reduce (mut, left) const diff = (x = {}, y = {}) => merge ( diff1 (x, y, "left") , diff1 (y, x, "right") ) const x = { a: 1, b: [ { c: 1 }, { d: 1 }, { e: 1 } ] } const y = { a: 1, b: [ { c: 2 }, { d: 1 }, 5, 6 ], z: 2 } console.log (diff (x, y)) // { b: // [ { c: { left: 1, right: 2 } } // , <1 empty item> // , { left: { e: 1 }, right: 5 } // , { right: 6 } // ] // , z: { right: 2 } // }
浅差异
此答案的先前版本提供了一个对象diff
函数,用于比较具有相同键的对象和比较具有不同键的对象,但两种解决方案都没有对嵌套对象递归执行差异。
递归交集
在这个相关的问答中,我们采用两个输入对象并计算递归intersect
而不是diff
。
解决方法很简单,
初始化你的数组,
var resultArray = [];
然后循环遍历对象的键,使用一个作为参考(高度假设对象具有相同的键,但您想检查具有不同值的键)
最后运行简单的代码
for(let key in obj){
// console.log(key);
if(obj[key] !== this.profileObject[key] ){
resultArray.push(key);
}
}
并在最后收集你的答案
console.log(resultArray);
这将返回第一个参数相对于第二个参数的差异。 不过,我没有在这里使用 angular.forEach。
var x = { a : 1, b:2, c :3, d:4 } var y = { a : 1, b:4, c :3, d : 5 }; var diff = function(x,y){ var target = {}; var diffProps = Object.keys(x).filter(function(i){ if(x[i] !== y[i]){ return true; } return false; }).map(function(j){ var obj = {}; obj[j] = x[j]; target = Object.assign(target,obj) }); return target; }; console.log(diff(x,y));
$scope.ar1 = {A: 10, B: 20, C: 30};
$scope.ar2 = {A: 10, B: 22, C: 30};
$scope.newObj = {};
angular.forEach($scope.ar1, function(v, i) {
// if ar2[i] is exists and ar2[i] != v then put that value to newObj
if ($scope.ar2[i] && $scope.ar2[i] != v) {
$scope.newObj[i] = $scope.ar2[i];
}
});
console.log($scope.newObj);
这是演示
这个解决方案不是有角度的,但它可能会有所帮助。
它将需要 2 个具有任意数量键的对象,并且它们不需要包含相同的键。
**输出:** The key:value pairs which are present in only one object and not the other and the key:value pairs which are present in both objects but the values are different.
var obj1 = {A: 10, B: 20, C: 30, E: 40};
var obj2 = {A: 11, B: 20, C: 30, D: 50};
var finalObject = {};
$( document ).ready(function() {
var keysOfObj1 = Object.keys( obj1 );
var keysOfObj2 = Object.keys( obj2 );
var keys = [];
keys = $( keysOfObj1 ).not( keysOfObj2 ).get(); // keys of first object not in second object
for( var i=0;i<keys.length;i++ ) {
finalObject[ keys[ i ] ] = obj1[ keys[ i ] ];
}
keys.length = 0; // reset the temp array
keys = $( keysOfObj2 ).not( keysOfObj1 ).get(); // keys of second object not in first object
for( var i=0;i<keys.length;i++ ) {
finalObject[ keys[ i ] ] = obj2[ keys[ i ] ];
}
keys.length = 0; // reset the temp array again
if( keysOfObj1.length != keysOfObj2.length ) {
// case already handled above
}
for( var i in obj1 ) {
if( obj1.hasOwnProperty( i ) ) {
if( obj2.hasOwnProperty( i ) ) {
if( obj1[ i ] != obj2[ i ] ) {
finalObject[ i ] = obj2[ i ];
} else {
// the property has the same value in both objects, all is well...
}
} else {
// case already handled above
}
} else {
// case already handled above
}
}
console.log( obj1 );
console.log( obj2 );
console.log( finalObject );
希望能帮助到你。
我希望这能帮到您。 我用jQuery each
函数做到了。
var a = {A: 10, B: 20, C: 30};
var b = {A: 10, B: 22, C: 30};
var hasObj = false; //Declaring variable outside for onetime memory allocation.
$.each(b, function(keyOfB, valOfB) {
hasObj = false; //Assigning false for each parent loop
$.each(a, function(keyOfA, valOfA) {
if (keyOfA == keyOfB && valOfA == valOfB) {
hasObj = true;
return false; //If key and value mathed loop will break and no remaining items of second array will be check.
}
});
if (hasObj == false) {
console.log(keyOfB + "--" + valOfB); //Printing the unmatched key and value
}
});
尝试这个
function getNewProperties(prevObj, newObj) {
const prevObjProperties = Object.keys(prevObj);
const newObjProperties = Object.keys(newObj);
const newProperties = newObjProperties.filter(prop => prevObjProperties.indexOf(prop) === -1);
return newProperties;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.