-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdictdiffer.js
128 lines (119 loc) · 3.99 KB
/
dictdiffer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* This function will take a patch (diffResult) object, as defined by the Python dictdiffer module:
* https://dictdiffer.readthedocs.io/en/latest/
* and will patch the destination JavaScript object. This is very useful when sending changes to objects shared between
* Python code and JavaScript code, like through a websocket connection.
*
* @param {object} diffResult - the output from the Python function dictdiffer.diff()
* @param {object} destination - the target JavaScript object. Note that prior to changes the dictionary on the Python
* side must match the JavaScript object. It is a one-way patch
* (Python dict -> JavaScript object).
* @returns {object} - returns the patched object. While not strictly necessary because the destination object is
* directly updated, it's useful to return the object in some scenarios.
*/
export function patch(diffResult, destination) {
function dotLookup(source, lookup, parent = false) {
if (!lookup) {
return source;
}
let value = source;
let keys;
if (Array.isArray(lookup)) {
// the lookup is a list of keys
keys = [...lookup];
} else if (typeof lookup === 'string' || lookup instanceof String) {
// the lookup is a string, split by "."
keys = lookup.split('.');
} else {
throw new Error(
'function dotLookup requires that the lookup parameter be either a string or an array'
);
}
if (parent) {
// Remove the last key
keys.pop();
}
keys.forEach((key, index, array) => {
if (Array.isArray(value)) {
key = parseInt(key);
}
value = value[key];
});
return value;
}
function add(node, changes) {
for (let idx = 0; idx < changes.length; idx++) {
let key = changes[idx][0];
let value = changes[idx][1];
let dest = dotLookup(destination, node);
if (Array.isArray(dest)) {
dest.splice(key, 0, value);
} else {
dest[key] = value;
}
}
}
function change(node, changes) {
let dest = dotLookup(destination, node, true);
let lastNode;
if (Array.isArray(node)) {
lastNode = node.pop();
} else {
lastNode = node.split('.').pop();
}
if (Array.isArray(dest)) {
lastNode = parseInt(lastNode);
}
dest[lastNode] = changes.pop();
}
function remove(node, changes) {
for (let idx = 0; idx < changes.length; idx++) {
let key = changes[idx][0];
let dest = dotLookup(destination, node);
if (Array.isArray(dest)) {
dest.splice(key, 1);
} else {
delete dest[key];
}
}
}
for (let idx = 0; idx < diffResult.length; idx++) {
let action = diffResult[idx][0];
let node = diffResult[idx][1];
let changes = diffResult[idx][2];
if (action === 'add') {
add(node, changes);
}
if (action === 'change') {
change(node, changes);
}
if (action === 'remove') {
remove(node, changes);
}
}
return destination;
}
/**
* This function will just run some tests against this library.
*/
function runTests() {
let testObject1 = { a: { b: { c: 3, list: [1, 2] } }, y: [100, 200, 300], remove_this: 'blah' };
let testObject2 = { a: { b: { c: 1, d: 2, list: [3, 4, 5] } }, z: 1000, y: [100, 200] };
let diffList = [
['change', ['a', 'b', 'list', 0], [1, 3]],
['change', ['a', 'b', 'list', 1], [2, 4]],
['change', 'a.b.c', [3, 1]],
['add', 'a.b.list', [[2, 5]]],
['add', 'a.b', [['d', 2]]],
['remove', 'y', [[2, 300]]],
['add', '', [['z', 1000]]],
['remove', '', [['remove_this', 'blah']]]
];
console.log('testObject1: ' + JSON.stringify(testObject1));
console.log('testObject2: ' + JSON.stringify(testObject2));
console.log('diffList: ' + JSON.stringify(diffList));
patch(diffList, testObject1);
console.log('Patch complete');
console.log('testObject1: ' + JSON.stringify(testObject1));
console.log('testObject2: ' + JSON.stringify(testObject2));
}