11cb0ef41Sopenharmony_ci/* 21cb0ef41Sopenharmony_ci const obj1 = {a: 4, b: 5}; 31cb0ef41Sopenharmony_ci const obj2 = {a: 3, b: 5}; 41cb0ef41Sopenharmony_ci const obj3 = {a: 4, c: 5}; 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ci diff(obj1, obj2); 71cb0ef41Sopenharmony_ci [ 81cb0ef41Sopenharmony_ci { "op": "replace", "path": ['a'], "value": 3 } 91cb0ef41Sopenharmony_ci ] 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ci diff(obj2, obj3); 121cb0ef41Sopenharmony_ci [ 131cb0ef41Sopenharmony_ci { "op": "remove", "path": ['b'] }, 141cb0ef41Sopenharmony_ci { "op": "replace", "path": ['a'], "value": 4 } 151cb0ef41Sopenharmony_ci { "op": "add", "path": ['c'], "value": 5 } 161cb0ef41Sopenharmony_ci ] 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci // using converter to generate jsPatch standard paths 191cb0ef41Sopenharmony_ci // see http://jsonpatch.com 201cb0ef41Sopenharmony_ci import {diff, jsonPatchPathConverter} from 'just-diff' 211cb0ef41Sopenharmony_ci diff(obj1, obj2, jsonPatchPathConverter); 221cb0ef41Sopenharmony_ci [ 231cb0ef41Sopenharmony_ci { "op": "replace", "path": '/a', "value": 3 } 241cb0ef41Sopenharmony_ci ] 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ci diff(obj2, obj3, jsonPatchPathConverter); 271cb0ef41Sopenharmony_ci [ 281cb0ef41Sopenharmony_ci { "op": "remove", "path": '/b' }, 291cb0ef41Sopenharmony_ci { "op": "replace", "path": '/a', "value": 4 } 301cb0ef41Sopenharmony_ci { "op": "add", "path": '/c', "value": 5 } 311cb0ef41Sopenharmony_ci ] 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci // arrays 341cb0ef41Sopenharmony_ci const obj4 = {a: 4, b: [1, 2, 3]}; 351cb0ef41Sopenharmony_ci const obj5 = {a: 3, b: [1, 2, 4]}; 361cb0ef41Sopenharmony_ci const obj6 = {a: 3, b: [1, 2, 4, 5]}; 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci diff(obj4, obj5); 391cb0ef41Sopenharmony_ci [ 401cb0ef41Sopenharmony_ci { "op": "replace", "path": ['a'], "value": 3 } 411cb0ef41Sopenharmony_ci { "op": "replace", "path": ['b', 2], "value": 4 } 421cb0ef41Sopenharmony_ci ] 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci diff(obj5, obj6); 451cb0ef41Sopenharmony_ci [ 461cb0ef41Sopenharmony_ci { "op": "add", "path": ['b', 3], "value": 5 } 471cb0ef41Sopenharmony_ci ] 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci // nested paths 501cb0ef41Sopenharmony_ci const obj7 = {a: 4, b: {c: 3}}; 511cb0ef41Sopenharmony_ci const obj8 = {a: 4, b: {c: 4}}; 521cb0ef41Sopenharmony_ci const obj9 = {a: 5, b: {d: 4}}; 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci diff(obj7, obj8); 551cb0ef41Sopenharmony_ci [ 561cb0ef41Sopenharmony_ci { "op": "replace", "path": ['b', 'c'], "value": 4 } 571cb0ef41Sopenharmony_ci ] 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci diff(obj8, obj9); 601cb0ef41Sopenharmony_ci [ 611cb0ef41Sopenharmony_ci { "op": "replace", "path": ['a'], "value": 5 } 621cb0ef41Sopenharmony_ci { "op": "remove", "path": ['b', 'c']} 631cb0ef41Sopenharmony_ci { "op": "add", "path": ['b', 'd'], "value": 4 } 641cb0ef41Sopenharmony_ci ] 651cb0ef41Sopenharmony_ci*/ 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_cifunction diff(obj1, obj2, pathConverter) { 681cb0ef41Sopenharmony_ci if (!obj1 || typeof obj1 != 'object' || !obj2 || typeof obj2 != 'object') { 691cb0ef41Sopenharmony_ci throw new Error('both arguments must be objects or arrays'); 701cb0ef41Sopenharmony_ci } 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci pathConverter || 731cb0ef41Sopenharmony_ci (pathConverter = function(arr) { 741cb0ef41Sopenharmony_ci return arr; 751cb0ef41Sopenharmony_ci }); 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci function getDiff({obj1, obj2, basePath, basePathForRemoves, diffs}) { 781cb0ef41Sopenharmony_ci var obj1Keys = Object.keys(obj1); 791cb0ef41Sopenharmony_ci var obj1KeysLength = obj1Keys.length; 801cb0ef41Sopenharmony_ci var obj2Keys = Object.keys(obj2); 811cb0ef41Sopenharmony_ci var obj2KeysLength = obj2Keys.length; 821cb0ef41Sopenharmony_ci var path; 831cb0ef41Sopenharmony_ci 841cb0ef41Sopenharmony_ci var lengthDelta = obj1.length - obj2.length; 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci if (trimFromRight(obj1, obj2)) { 871cb0ef41Sopenharmony_ci for (var i = 0; i < obj1KeysLength; i++) { 881cb0ef41Sopenharmony_ci var key = Array.isArray(obj1) ? Number(obj1Keys[i]) : obj1Keys[i]; 891cb0ef41Sopenharmony_ci if (!(key in obj2)) { 901cb0ef41Sopenharmony_ci path = basePathForRemoves.concat(key); 911cb0ef41Sopenharmony_ci diffs.remove.push({ 921cb0ef41Sopenharmony_ci op: 'remove', 931cb0ef41Sopenharmony_ci path: pathConverter(path), 941cb0ef41Sopenharmony_ci }); 951cb0ef41Sopenharmony_ci } 961cb0ef41Sopenharmony_ci } 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci for (var i = 0; i < obj2KeysLength; i++) { 991cb0ef41Sopenharmony_ci var key = Array.isArray(obj2) ? Number(obj2Keys[i]) : obj2Keys[i]; 1001cb0ef41Sopenharmony_ci pushReplaces({ 1011cb0ef41Sopenharmony_ci key, 1021cb0ef41Sopenharmony_ci obj1, 1031cb0ef41Sopenharmony_ci obj2, 1041cb0ef41Sopenharmony_ci path: basePath.concat(key), 1051cb0ef41Sopenharmony_ci pathForRemoves: basePath.concat(key), 1061cb0ef41Sopenharmony_ci diffs, 1071cb0ef41Sopenharmony_ci }); 1081cb0ef41Sopenharmony_ci } 1091cb0ef41Sopenharmony_ci } else { 1101cb0ef41Sopenharmony_ci // trim from left, objects are both arrays 1111cb0ef41Sopenharmony_ci for (var i = 0; i < lengthDelta; i++) { 1121cb0ef41Sopenharmony_ci path = basePathForRemoves.concat(i); 1131cb0ef41Sopenharmony_ci diffs.remove.push({ 1141cb0ef41Sopenharmony_ci op: 'remove', 1151cb0ef41Sopenharmony_ci path: pathConverter(path), 1161cb0ef41Sopenharmony_ci }); 1171cb0ef41Sopenharmony_ci } 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci // now make a copy of obj1 with excess elements left trimmed and see if there any replaces 1201cb0ef41Sopenharmony_ci var obj1Trimmed = obj1.slice(lengthDelta);; 1211cb0ef41Sopenharmony_ci for (var i = 0; i < obj2KeysLength; i++) { 1221cb0ef41Sopenharmony_ci pushReplaces({ 1231cb0ef41Sopenharmony_ci key: i, 1241cb0ef41Sopenharmony_ci obj1: obj1Trimmed, 1251cb0ef41Sopenharmony_ci obj2, 1261cb0ef41Sopenharmony_ci path: basePath.concat(i), 1271cb0ef41Sopenharmony_ci // since list of removes are reversed before presenting result, 1281cb0ef41Sopenharmony_ci // we need to ignore existing parent removes when doing nested removes 1291cb0ef41Sopenharmony_ci pathForRemoves: basePath.concat(i + lengthDelta), 1301cb0ef41Sopenharmony_ci diffs, 1311cb0ef41Sopenharmony_ci }); 1321cb0ef41Sopenharmony_ci } 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci } 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_ci var diffs = {remove: [], replace: [], add: []}; 1371cb0ef41Sopenharmony_ci getDiff({ 1381cb0ef41Sopenharmony_ci obj1, 1391cb0ef41Sopenharmony_ci obj2, 1401cb0ef41Sopenharmony_ci basePath: [], 1411cb0ef41Sopenharmony_ci basePathForRemoves: [], 1421cb0ef41Sopenharmony_ci diffs, 1431cb0ef41Sopenharmony_ci }); 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci // reverse removes since we want to maintain indexes 1461cb0ef41Sopenharmony_ci return diffs.remove 1471cb0ef41Sopenharmony_ci .reverse() 1481cb0ef41Sopenharmony_ci .concat(diffs.replace) 1491cb0ef41Sopenharmony_ci .concat(diffs.add); 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci function pushReplaces({key, obj1, obj2, path, pathForRemoves, diffs}) { 1521cb0ef41Sopenharmony_ci var obj1AtKey = obj1[key]; 1531cb0ef41Sopenharmony_ci var obj2AtKey = obj2[key]; 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci if(!(key in obj1) && (key in obj2)) { 1561cb0ef41Sopenharmony_ci var obj2Value = obj2AtKey; 1571cb0ef41Sopenharmony_ci diffs.add.push({ 1581cb0ef41Sopenharmony_ci op: 'add', 1591cb0ef41Sopenharmony_ci path: pathConverter(path), 1601cb0ef41Sopenharmony_ci value: obj2Value, 1611cb0ef41Sopenharmony_ci }); 1621cb0ef41Sopenharmony_ci } else if(obj1AtKey !== obj2AtKey) { 1631cb0ef41Sopenharmony_ci if(Object(obj1AtKey) !== obj1AtKey || 1641cb0ef41Sopenharmony_ci Object(obj2AtKey) !== obj2AtKey || differentTypes(obj1AtKey, obj2AtKey) 1651cb0ef41Sopenharmony_ci ) { 1661cb0ef41Sopenharmony_ci pushReplace(path, diffs, obj2AtKey); 1671cb0ef41Sopenharmony_ci } else { 1681cb0ef41Sopenharmony_ci if(!Object.keys(obj1AtKey).length && 1691cb0ef41Sopenharmony_ci !Object.keys(obj2AtKey).length && 1701cb0ef41Sopenharmony_ci String(obj1AtKey) != String(obj2AtKey)) { 1711cb0ef41Sopenharmony_ci pushReplace(path, diffs, obj2AtKey); 1721cb0ef41Sopenharmony_ci } else { 1731cb0ef41Sopenharmony_ci getDiff({ 1741cb0ef41Sopenharmony_ci obj1: obj1[key], 1751cb0ef41Sopenharmony_ci obj2: obj2[key], 1761cb0ef41Sopenharmony_ci basePath: path, 1771cb0ef41Sopenharmony_ci basePathForRemoves: pathForRemoves, 1781cb0ef41Sopenharmony_ci diffs}); 1791cb0ef41Sopenharmony_ci } 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci } 1821cb0ef41Sopenharmony_ci } 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci function pushReplace(path, diffs, newValue) { 1851cb0ef41Sopenharmony_ci diffs.replace.push({ 1861cb0ef41Sopenharmony_ci op: 'replace', 1871cb0ef41Sopenharmony_ci path: pathConverter(path), 1881cb0ef41Sopenharmony_ci value: newValue, 1891cb0ef41Sopenharmony_ci }); 1901cb0ef41Sopenharmony_ci } 1911cb0ef41Sopenharmony_ci} 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_cifunction jsonPatchPathConverter(arrayPath) { 1941cb0ef41Sopenharmony_ci return [''].concat(arrayPath).join('/'); 1951cb0ef41Sopenharmony_ci} 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_cifunction differentTypes(a, b) { 1981cb0ef41Sopenharmony_ci return Object.prototype.toString.call(a) != Object.prototype.toString.call(b); 1991cb0ef41Sopenharmony_ci} 2001cb0ef41Sopenharmony_ci 2011cb0ef41Sopenharmony_cifunction trimFromRight(obj1, obj2) { 2021cb0ef41Sopenharmony_ci var lengthDelta = obj1.length - obj2.length; 2031cb0ef41Sopenharmony_ci if (Array.isArray(obj1) && Array.isArray(obj2) && lengthDelta > 0) { 2041cb0ef41Sopenharmony_ci var leftMatches = 0; 2051cb0ef41Sopenharmony_ci var rightMatches = 0; 2061cb0ef41Sopenharmony_ci for (var i = 0; i < obj2.length; i++) { 2071cb0ef41Sopenharmony_ci if (String(obj1[i]) === String(obj2[i])) { 2081cb0ef41Sopenharmony_ci leftMatches++; 2091cb0ef41Sopenharmony_ci } else { 2101cb0ef41Sopenharmony_ci break; 2111cb0ef41Sopenharmony_ci } 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci for (var j = obj2.length; j > 0; j--) { 2141cb0ef41Sopenharmony_ci if (String(obj1[j + lengthDelta]) === String(obj2[j])) { 2151cb0ef41Sopenharmony_ci rightMatches++; 2161cb0ef41Sopenharmony_ci } else { 2171cb0ef41Sopenharmony_ci break; 2181cb0ef41Sopenharmony_ci } 2191cb0ef41Sopenharmony_ci } 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci // bias to trim right becase it requires less index shifting 2221cb0ef41Sopenharmony_ci return leftMatches >= rightMatches; 2231cb0ef41Sopenharmony_ci } 2241cb0ef41Sopenharmony_ci return true; 2251cb0ef41Sopenharmony_ci} 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ciexport {diff, jsonPatchPathConverter}; 228