I need to refactor my program to take a JSON obj, store it somewhere, make a bunch of changes, then pare the two objects to see what has been changed, deleted, and added.
I'm not sure of a way to do this in JS, so could anyone advise a way to do this in Angular (the object parison part)? Otherwise, I'm going to have to make a ton of changes to the way my program runs / try it from the backend. Appreciate any help.
I need to refactor my program to take a JSON obj, store it somewhere, make a bunch of changes, then pare the two objects to see what has been changed, deleted, and added.
I'm not sure of a way to do this in JS, so could anyone advise a way to do this in Angular (the object parison part)? Otherwise, I'm going to have to make a ton of changes to the way my program runs / try it from the backend. Appreciate any help.
Share Improve this question asked Oct 27, 2015 at 19:46 Dave MDave M 4181 gold badge3 silver badges16 bronze badges 6- stackoverflow./questions/20409712/pare-objects-in-angular – aminner Commented Oct 27, 2015 at 19:52
- I don't need equality. I need the difference between the objects – Dave M Commented Oct 27, 2015 at 19:54
- Short of iterating over each object there's no easy way to acplish this. You can iterate over each property in each subobject (think recursive call) such as what's in this link stackoverflow./questions/8312459/… – aminner Commented Oct 27, 2015 at 19:57
- That's sort of what I feared. Unfortunately, that would be a real mess with the json structure that I have. – Dave M Commented Oct 27, 2015 at 20:01
-
1
library
js-data
might help you. It tracks changes – charlietfl Commented Oct 27, 2015 at 20:07
2 Answers
Reset to default 2The only built in operation for object parison is the ==
/===
equality operators, which use reference equality: A is B, rather than A is equal to B.
What you want is a list of change descriptors describing the difference between two objects.
As identified in the ments, this is going to need a recursive traversal of the objects, using a mixture of reference and existence checks.
The following algorithm is a quick implementation of an idea. The objects are traversed and their changes are described with a list of objects. Just like the objects themselves, the changes are nested, but have a unique id, based on their location within the object (so it could be flattened).
function diff(a, b, namespace) {
namespace = (namespace || '') + '.';
var keysInA = Object.keys(a),
keysInB = Object.keys(b);
var diffA = keysInA.reduce(function(changes, key) {
var ns = namespace + key;
if(typeof b[key] == 'undefined') {
return changes.concat([{ type: 'DELETED', id: ns }]);
}
if(a[key] !== b[key]) {
return changes.concat([{ type: 'CHANGED', id: ns }]);
}
if(typeof a[key] == b[key] == 'object') {
return diff(a[key], b[key], ns);
}
return changes;
}, []);
var diffB = keysInB.reduce(function(changes, key) {
var ns = namespace + key;
if(typeof a[key] == 'undefined') {
return changes.concat([{ type: 'ADDED', id: ns }]);
}
return changes;
}, []);
return diffA.concat(diffB);
}
For example we take the original state of an object.
var a = { a: 1, b: 2, c: 3 };
And the new state.
var b = { a: 2, c: 3, d: 5 };
Then run them with the diff
function.
diff(a, b);
It returns a list of the changes.
[
{ id: '.a', type: 'CHANGED' },
{ id: '.b', type: 'DELETED' },
{ id: '.d', type: 'ADDED' }
]
Obviously, you would have to adapt this algorithm to make it fit your criteria for what constitutes a change. You might want to look at deep equality, rather than paring references the whole way down.
I'll add my implementation of Dan's suggestion here in case it will help someone who wants to see an actual implementation:
var propertiesToIgnore = ['.indexesTracked', '.notInSyncWithDb', '.date', '.details.updatedAt', '.autoLoadLocalStorage', '.deletionQueue']; //these are extraneous properties added to project that should not be represented in interface (and not tracked)
var keysToIgnore = ['addAllDatacuts', 'datacutNames']; // this just looks at the property rather than the above which matches from the project root
function diff(a, b, namespace, firstCall) {
namespace = firstCall ? (namespace || '') : (namespace || '') + '.';
var keysInA = Object.keys(a),
keysInB = Object.keys(b);
var diffA = keysInA.reduce(function(changes, key) {
var ns = namespace + key;
if (propertiesToIgnore.indexOf(ns) !== -1 || keysToIgnore.indexOf(key) !== -1) {
return changes;
}
if (key == '$$hashKey') {
return changes;
}
if (angular.equals(a[key], b[key])) { // whole chain is equal so I do not need to iterate over this branch
return changes;
}
if (typeof b[key] == 'undefined') {
return changes.concat([{ type: 'DELETED', id: ns }]);
}
if (a[key] !== b[key] && (typeof b[key] !== 'object' || typeof a[key] !== 'object')) {
return changes.concat([{ type: 'UPDATED', id: ns }]);
}
if (typeof b[key] === 'object' && typeof a[key] === 'object') {
return changes.concat(diff(a[key], b[key], ns));
}
if (a[key] === null || b[key] === null) { // avoids values that are null as js considers null an object
return changes;
}
if(typeof a[key] == 'object' && typeof b[key] == 'object' && typeof a[key].getMonth !== 'function' && typeof b[key].getMonth !== 'function') { // last part necessary to make sure it is not a date object
return diff(a[key], b[key], ns);
}
return changes;
}, []);
var diffB = keysInB.reduce(function(changes, key) {
var ns = namespace + key;
if (propertiesToIgnore.indexOf(ns) !== -1 || keysToIgnore.indexOf(key) !== -1) {
return changes;
}
if (key == '$$hashKey') {
return changes;
}
if (typeof a[key] == 'undefined') {
return changes.concat([{ type: 'ADDED', id: ns }]);
}
return changes;
}, []);
return diffA.concat(diffB);
}
$scope.changes = diff(dbData, $scope.project, '');
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744907674a4600354.html
评论列表(0条)