두 개의 객체가 있습니다 : oldObj
및 newObj
.
의 데이터 oldObj
는 양식을 채우는 데 사용되었으며 newObj
사용자가이 양식의 데이터를 변경하여 제출 한 결과입니다.
두 물체는 깊습니다. 그들은 객체 또는 객체의 배열 등의 속성을 가지고 있습니다-n 레벨 깊이가 될 수 있으므로 diff 알고리즘은 재귀 적이어야합니다.
지금은 단지에서 (추가와 같이 / 업데이트 / 삭제) 변경이 있었는지 알아낼 필요 oldObj
에 newObj
뿐만 아니라, 최선을 다해 그것을 표현하는 방법.
지금까지 내 생각은 genericDeepDiffBetweenObjects
폼에 객체를 반환하는 메서드를 작성하는 {add:{...},upd:{...},del:{...}}
것이지만 생각했습니다. 다른 사람이 전에 이것을 필요로 했어야합니다.
그래서 … 누구든지 이것을 할 라이브러리 또는 코드 조각을 알고 있습니까 (아직 JSON 직렬화 가능한 방식으로) 차이를 나타내는 더 나은 방법이 있습니까?
최신 정보:
와 동일한 객체 구조를 사용 newObj
하지만 모든 속성 값을 양식의 객체로 변환 하여 업데이트 된 데이터를 나타내는 더 좋은 방법을 생각했습니다 .
{type: '<update|create|delete>', data: <propertyValue>}
경우에 따라서 newObj.prop1 = 'new value'
그리고 oldObj.prop1 = 'old value'
그것을 설정합니다returnObj.prop1 = {type: 'update', data: 'new value'}
업데이트 2 :
그것은 배열이 있기 때문에 우리가 배열 인 속성에 도착하면 진정으로 털이 얻을 [1,2,3]
동일로 간주되어야한다 [2,3,1]
문자열, INT 및 부울과 같은 값 기반 형의 배열에 대한 간단한 충분히 인,하지만 올 때 처리하기 정말 어려운 도착 객체 및 배열과 같은 참조 유형의 배열.
동일해야하는 배열 예 :
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
이러한 유형의 깊은 가치 평등을 확인하는 것뿐만 아니라 변경 사항을 나타내는 좋은 방법을 찾는 것도 매우 복잡합니다.
답변
나는 당신이 원하는 것을하고있는 작은 수업을 썼으며 여기서 테스트 할 수 있습니다 .
귀하의 제안과 다른 점은 [1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
동일한 것으로 간주하지 않는다는 것입니다. 요소의 순서가 같지 않으면 배열이 같지 않다고 생각하기 때문입니다. 물론 필요한 경우 변경할 수 있습니다. 또한이 코드는 전달 된 기본 값 (이 작업은 “compareValues”메소드에 의해 수행됨)에 따라 임의의 방식으로 diff 오브젝트를 형식화하는 데 사용되는 인수로 기능을 수행하도록 추가로 향상 될 수 있습니다.
var deepDiffMapper = function () {
return {
VALUE_CREATED: 'created',
VALUE_UPDATED: 'updated',
VALUE_DELETED: 'deleted',
VALUE_UNCHANGED: 'unchanged',
map: function(obj1, obj2) {
if (this.isFunction(obj1) || this.isFunction(obj2)) {
throw 'Invalid argument. Function given, object expected.';
}
if (this.isValue(obj1) || this.isValue(obj2)) {
return {
type: this.compareValues(obj1, obj2),
data: obj1 === undefined ? obj2 : obj1
};
}
var diff = {};
for (var key in obj1) {
if (this.isFunction(obj1[key])) {
continue;
}
var value2 = undefined;
if (obj2[key] !== undefined) {
value2 = obj2[key];
}
diff[key] = this.map(obj1[key], value2);
}
for (var key in obj2) {
if (this.isFunction(obj2[key]) || diff[key] !== undefined) {
continue;
}
diff[key] = this.map(undefined, obj2[key]);
}
return diff;
},
compareValues: function (value1, value2) {
if (value1 === value2) {
return this.VALUE_UNCHANGED;
}
if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
return this.VALUE_UNCHANGED;
}
if (value1 === undefined) {
return this.VALUE_CREATED;
}
if (value2 === undefined) {
return this.VALUE_DELETED;
}
return this.VALUE_UPDATED;
},
isFunction: function (x) {
return Object.prototype.toString.call(x) === '[object Function]';
},
isArray: function (x) {
return Object.prototype.toString.call(x) === '[object Array]';
},
isDate: function (x) {
return Object.prototype.toString.call(x) === '[object Date]';
},
isObject: function (x) {
return Object.prototype.toString.call(x) === '[object Object]';
},
isValue: function (x) {
return !this.isObject(x) && !this.isArray(x);
}
}
}();
var result = deepDiffMapper.map({
a: 'i am unchanged',
b: 'i am deleted',
e: {
a: 1,
b: false,
c: null
},
f: [1, {
a: 'same',
b: [{
a: 'same'
}, {
d: 'delete'
}]
}],
g: new Date('2017.11.25')
}, {
a: 'i am unchanged',
c: 'i am created',
e: {
a: '1',
b: '',
d: 'created'
},
f: [{
a: 'same',
b: [{
a: 'same'
}, {
c: 'create'
}]
}, 1],
g: new Date('2017.11.25')
});
console.log(result);
답변
밑줄을 사용하여 간단한 차이점 :
var o1 = {a: 1, b: 2, c: 2},
o2 = {a: 2, b: 1, c: 2};
_.omit(o1, function(v,k) { return o2[k] === v; })
o1
해당 부분 이 일치하지만 다음과 같은 값이 다릅니다 o2
.
{a: 1, b: 2}
깊은 차이가 있으면 다릅니다.
function diff(a,b) {
var r = {};
_.each(a, function(v,k) {
if(b[k] === v) return;
// but what if it returns an empty object? still attach?
r[k] = _.isObject(v)
? _.diff(v, b[k])
: v
;
});
return r;
}
주석에서 @Juhana가 지적한 것처럼 위의 내용은 diff a-> b이며 되돌릴 수 없습니다 (b의 추가 속성은 무시 됨). 대신 a-> b-> a를 사용하십시오.
(function(_) {
function deepDiff(a, b, r) {
_.each(a, function(v, k) {
// already checked this or equal...
if (r.hasOwnProperty(k) || b[k] === v) return;
// but what if it returns an empty object? still attach?
r[k] = _.isObject(v) ? _.diff(v, b[k]) : v;
});
}
/* the function */
_.mixin({
diff: function(a, b) {
var r = {};
deepDiff(a, b, r);
deepDiff(b, a, r);
return r;
}
});
})(_.noConflict());
전체 example + tests + mixins에 대해서는 http://jsfiddle.net/drzaus/9g5qoxwj/ 를 참조하십시오
답변
ES6 솔루션을 제공하고 싶습니다 … 단방향 diff o2
입니다 o1
.
let o1 = {
one: 1,
two: 2,
three: 3
}
let o2 = {
two: 2,
three: 3,
four: 4
}
let diff = Object.keys(o2).reduce((diff, key) => {
if (o1[key] === o2[key]) return diff
return {
...diff,
[key]: o2[key]
}
}, {})
답변
Lodash 사용하기 :
_.mergeWith(oldObj, newObj, function (objectValue, sourceValue, key, object, source) {
if ( !(_.isEqual(objectValue, sourceValue)) && (Object(objectValue) !== objectValue)) {
console.log(key + "\n Expected: " + sourceValue + "\n Actual: " + objectValue);
}
});
키 / 객체 / 소스를 사용하지 않지만 액세스 해야하는 경우 키를 남겨 두었습니다. 객체 비교는 콘솔이 가장 바깥 쪽 요소에서 가장 안쪽 요소까지 콘솔의 차이점을 인쇄하지 못하게합니다.
배열을 처리하기 위해 내부에 논리를 추가 할 수 있습니다. 아마도 배열을 먼저 정렬하십시오. 이것은 매우 유연한 솔루션입니다.
편집하다
lodash 업데이트로 인해 _.merge에서 _.mergeWith로 변경되었습니다. 변경 사항을 확인한 Aviron에게 감사합니다.
답변
다음은 두 JavaScript 객체 사이의 차이점을 찾는 데 사용할 수있는 JavaScript 라이브러리입니다.
Github URL :
https://github.com/cosmicanant/recursive-diff
Npmjs URL : https://www.npmjs.com/package/recursive-diff
브라우저와 Node.js에서 재귀 -diff 라이브러리를 사용할 수 있습니다. 브라우저의 경우 다음을 수행하십시오.
<script type="text" src="https://unpkg.com/recursive-diff@1.0.0/dist/recursive-diff.min.js"/>
<script type="text/javascript">
const ob1 = {a:1, b: [2,3]};
const ob2 = {a:2, b: [3,3,1]};
const delta = recursiveDiff.getDiff(ob1,ob2);
/* console.log(delta) will dump following data
[
{path: ['a'], op: 'update', val: 2}
{path: ['b', '0'], op: 'update',val: 3},
{path: ['b',2], op: 'add', val: 1 },
]
*/
const ob3 = recursiveDiff.applyDiff(ob1, delta); //expect ob3 is deep equal to ob2
</script>
node.js에서 ‘recursive-diff’모듈이 필요하고 아래처럼 사용할 수 있습니다.
const diff = require('recursive-diff');
const ob1 = {a: 1}, ob2: {b:2};
const diff = diff.getDiff(ob1, ob2);
답변
요즘에는 사용할 수있는 모듈이 꽤 있습니다. 내가 찾은 수많은 diffing 모듈에 만족하지 않았기 때문에 최근 에이 작업을 수행하기 위해 모듈을 작성했습니다. 해당 호출 odiff
: https://github.com/Tixit/odiff . 또한 가장 인기있는 모듈과 readme에서 허용되지 않는 이유를 나열했습니다 . 원하는 속성이없는 odiff
경우 살펴볼 수 있습니다 odiff
. 예를 들면 다음과 같습니다.
var a = [{a:1,b:2,c:3}, {x:1,y: 2, z:3}, {w:9,q:8,r:7}]
var b = [{a:1,b:2,c:3},{t:4,y:5,u:6},{x:1,y:'3',z:3},{t:9,y:9,u:9},{w:9,q:8,r:7}]
var diffs = odiff(a,b)
/* diffs now contains:
[{type: 'add', path:[], index: 2, vals: [{t:9,y:9,u:9}]},
{type: 'set', path:[1,'y'], val: '3'},
{type: 'add', path:[], index: 1, vals: [{t:4,y:5,u:6}]}
]
*/
답변
const diff = require("deep-object-diff").diff;
let differences = diff(obj2, obj1);
매주 5 만 회 이상 다운로드되는 npm 모듈이 있습니다 : https://www.npmjs.com/package/deep-object-diff
차이점의 표현과 같은 객체를 좋아합니다. 특히 형식이 지정되면 구조를 쉽게 볼 수 있습니다.
const diff = require("deep-object-diff").diff;
const lhs = {
foo: {
bar: {
a: ['a', 'b'],
b: 2,
c: ['x', 'y'],
e: 100 // deleted
}
},
buzz: 'world'
};
const rhs = {
foo: {
bar: {
a: ['a'], // index 1 ('b') deleted
b: 2, // unchanged
c: ['x', 'y', 'z'], // 'z' added
d: 'Hello, world!' // added
}
},
buzz: 'fizz' // updated
};
console.log(diff(lhs, rhs)); // =>
/*
{
foo: {
bar: {
a: {
'1': undefined
},
c: {
'2': 'z'
},
d: 'Hello, world!',
e: undefined
}
},
buzz: 'fizz'
}
*/