Open yangwch opened 4 years ago
Another piece of code to reproduce the bug:
import * as jsondiffpatch from 'jsondiffpatch';
const a = [
{uid: 'scene', parent_uid: null},
{uid: 'txt_clztu14dm00042v6j71u1c9k3', parent_uid: 'scene'},
{uid: 'grp_cm0vzowcd00022v6izleecn6c', parent_uid: 'scene'},
{uid: 'txt_cm01a5cpi00012v6inbaunav1', parent_uid: 'grp_cm0vzowcd00022v6izleecn6c'},
{uid: 'txt_cm097vd9000012v6i9tsfrff2', parent_uid: 'grp_cm0vzowcd00022v6izleecn6c'},
{uid: 'txt_cm097w3zp00022v6iek5pr7id', parent_uid: 'grp_cm0vzowcd00022v6izleecn6c'},
{uid: 'fdr_cm0ukrqom00002v6ixqokqydk', parent_uid: 'scene'},
{uid: 'fdr_cm0umi0e000002v6ivbkyzpwf', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm0bc6t0900022v6ij7baxq2d', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm0bc70dh00032v6im4wawi5v', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'fdr_cm0vznwt800012v6ial614xz1', parent_uid: 'scene'},
{uid: 'txt_cm0bc5aid00002v6iu6e4a0m5', parent_uid: 'scene'}
];
const b = [
{uid: 'scene', parent_uid: null},
{uid: 'txt_clztu14dm00042v6j71u1c9k3', parent_uid: 'scene'},
{uid: 'txt_cm01a5cpi00012v6inbaunav1', parent_uid: 'scene'},
{uid: 'fdr_cm0ukrqom00002v6ixqokqydk', parent_uid: 'scene'},
{uid: 'fdr_cm0umi0e000002v6ivbkyzpwf', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm097vd9000012v6i9tsfrff2', parent_uid: 'fdr_cm0umi0e000002v6ivbkyzpwf'},
{uid: 'txt_cm097w3zp00022v6iek5pr7id', parent_uid: 'fdr_cm0umi0e000002v6ivbkyzpwf'},
{uid: 'txt_cm0bc6t0900022v6ij7baxq2d', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm0bc70dh00032v6im4wawi5v', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'fdr_cm0vznwt800012v6ial614xz1', parent_uid: 'scene'},
{uid: 'txt_cm0bc5aid00002v6iu6e4a0m5', parent_uid: 'scene'}
];
const dd = jsondiffpatch.create({
objectHash: function (item, i) {
return item.uid || JSON.stringify(item);
},
});
const diff = dd.diff(a, b);
const c = dd.unpatch(JSON.parse(JSON.stringify(b)), diff);
const cc = unpatch(JSON.parse(JSON.stringify(b)), diff);
console.log(dd.diff(a, c));
console.log(dd.diff(a, cc));
function unpatch(obj, diff)
{
// 1. Restore property values in obj.
Object.keys(diff).forEach(function (key) {
if (key[0] === '_') {
return;
}
unpatch_props(obj[key], diff[key]);
});
// 2. Move items to their original positions
let ii = 0;
Object.keys(diff).forEach(function (key) {
const m = key.match(/^_(\d+)$/);
if (!m || diff[key][2] !== 3) {
return;
}
const i_prev = parseInt(m[1]);
const i_cur = diff[key][1];
obj.splice(i_prev - ii, 0, ...obj.splice(i_cur - ii, 1));
++ii;
});
// 3. Insert removed items
ii = 0;
Object.keys(diff).forEach(function (key) {
const m = key.match(/^_(\d+)$/);
if (!m || diff[key][2] !== 0) {
return;
}
const i_prev = parseInt(m[1]);
obj.splice(i_prev - ii, 0, diff[key][0]);
++ii;
});
return obj;
}
function unpatch_props(obj, props)
{
Object.keys(props).forEach(function (key) {
const [prev, cur] = props[key];
if (obj[key] !== cur) {
throw new Error('Assertion failed');
}
obj[key] = prev;
});
}
Result:
{
'5': {
parent_uid: [
'grp_cm0vzowcd00022v6izleecn6c',
'fdr_cm0umi0e000002v6ivbkyzpwf'
]
},
'6': { parent_uid: [ 'scene', 'grp_cm0vzowcd00022v6izleecn6c' ] },
_t: 'a'
}
undefined
It seems that the problem is in reverse
: reverse(diff(a, b))
should return the same result as diff(b, a)
, but results are different.
In other words, unpatch(b, diff(a, b))
, which is just patch(b, reverse(diff(a, b))
, does not produce a
, but patch(b, diff(b, a))
does.
A temporary solution is to turn off array.detectMove
option. That way diffs would be bigger, but unpatch
would work correctly:
import * as jsondiffpatch from 'jsondiffpatch';
const a = [
{uid: 'scene', parent_uid: null},
{uid: 'txt_clztu14dm00042v6j71u1c9k3', parent_uid: 'scene'},
{uid: 'grp_cm0vzowcd00022v6izleecn6c', parent_uid: 'scene'},
{uid: 'txt_cm01a5cpi00012v6inbaunav1', parent_uid: 'grp_cm0vzowcd00022v6izleecn6c'},
{uid: 'txt_cm097vd9000012v6i9tsfrff2', parent_uid: 'grp_cm0vzowcd00022v6izleecn6c'},
{uid: 'txt_cm097w3zp00022v6iek5pr7id', parent_uid: 'grp_cm0vzowcd00022v6izleecn6c'},
{uid: 'fdr_cm0ukrqom00002v6ixqokqydk', parent_uid: 'scene'},
{uid: 'fdr_cm0umi0e000002v6ivbkyzpwf', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm0bc6t0900022v6ij7baxq2d', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm0bc70dh00032v6im4wawi5v', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'fdr_cm0vznwt800012v6ial614xz1', parent_uid: 'scene'},
{uid: 'txt_cm0bc5aid00002v6iu6e4a0m5', parent_uid: 'scene'}
];
const b = [
{uid: 'scene', parent_uid: null},
{uid: 'txt_clztu14dm00042v6j71u1c9k3', parent_uid: 'scene'},
{uid: 'txt_cm01a5cpi00012v6inbaunav1', parent_uid: 'scene'},
{uid: 'fdr_cm0ukrqom00002v6ixqokqydk', parent_uid: 'scene'},
{uid: 'fdr_cm0umi0e000002v6ivbkyzpwf', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm097vd9000012v6i9tsfrff2', parent_uid: 'fdr_cm0umi0e000002v6ivbkyzpwf'},
{uid: 'txt_cm097w3zp00022v6iek5pr7id', parent_uid: 'fdr_cm0umi0e000002v6ivbkyzpwf'},
{uid: 'txt_cm0bc6t0900022v6ij7baxq2d', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'txt_cm0bc70dh00032v6im4wawi5v', parent_uid: 'fdr_cm0ukrqom00002v6ixqokqydk'},
{uid: 'fdr_cm0vznwt800012v6ial614xz1', parent_uid: 'scene'},
{uid: 'txt_cm0bc5aid00002v6iu6e4a0m5', parent_uid: 'scene'}
];
const dd = jsondiffpatch.create({
objectHash: function (item, i) {
return item.uid || JSON.stringify(item);
},
arrays: {
detectMove: false,
},
});
const diff = dd.diff(a, b);
const c = dd.unpatch(JSON.parse(JSON.stringify(b)), diff);
console.log(dd.diff(a, c));
Until pull request will be accepted, fixed versions of reverse
and unpatch
could be taken from:
my code
result