-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdiff.js
More file actions
82 lines (78 loc) · 1.87 KB
/
diff.js
File metadata and controls
82 lines (78 loc) · 1.87 KB
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
/**
* Use this to create a "patch" object in the worker thread.
* @template {any} T
* @template {any} U
* @param {T} obj
* @param {U} old
* @returns {T | (Partial<T> | Partial<U>) | undefined}
* @example
* patch = diff(newObject, oldObject);
*/
export function diff(obj, old) {
if (typeof obj === 'object') {
const isArray = Array.isArray(obj);
if (!old || typeof old !== 'object' || isArray !== Array.isArray(old)) {
return obj;
}
if (isArray) {
let out;
let i = 0;
const max = Math.min(obj.length, old.length);
for (; i < max; i++) {
const differs = different(obj[i], old[i]);
if (differs) break;
}
// for previously-empty arrays, hint at newness by using an Array
const useArray = old.length === 0;
const offset = obj.length - old.length;
for (let j = obj.length; j-- > i; ) {
const oldJ = j - offset;
if (oldJ >= 0) {
const differs = different(obj[j], old[oldJ]);
if (differs) {
if (!out) out = useArray ? [] : {};
out[j] = diff(obj[j], old[oldJ]);
}
} else {
if (!out) out = useArray ? [] : {};
out[j] = obj[j];
}
}
return out;
}
let out;
for (let key in obj) {
if (!(key in old) || obj[key] !== old[key]) {
if (!out) out = {};
// `undefined` means removed, missing means unchanged.
const r = diff(obj[key], old[key]);
if (r !== undefined) {
out[key] = r;
}
}
}
for (let key in old) {
if (obj == null || !(key in obj)) {
if (!out) out = {};
// `undefined` means removed, missing means unchanged.
out[key] = undefined;
}
}
return out;
} else if (obj != old) {
return obj;
}
}
function different(obj, old) {
for (let key in obj) {
if (old == null || !(key in old) || obj[key] !== old[key]) {
return true;
}
}
for (let key in old) {
if (obj == null || !(key in obj)) {
return true;
}
}
return false;
}