{"id":2188,"date":"2019-05-21T10:22:14","date_gmt":"2019-05-21T09:22:14","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=2188"},"modified":"2021-02-10T16:12:47","modified_gmt":"2021-02-10T15:12:47","slug":"get-object-diff","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/get-object-diff\/","title":{"rendered":"Get object diff"},"content":{"rendered":"\n<p>Compare JSON object properties for new\/changed\/deleted<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">(_ => {\n\tfunction filterUnique() {\n\t\tconst keys = new Set();\n\t\treturn (key) => !keys.has(key) ? keys.add(key) || true : false;\n\t}\n\n\tfunction* fullOuterJoin(left, right, keyFn, selectFn) {\n\t\tconst leftLookup = {};\n\t\tconst rightLookup = {};\n\t\tleft.forEach(x => leftLookup[keyFn(x)] = x);\n\t\tright.forEach(x => rightLookup[keyFn(x)] = x);\n\t\tconst unique = filterUnique();\n\t\tconst keys = [...left.map(x => keyFn(x)), ...right.map(x => keyFn(x))].filter(unique);\n\t\tfor (const key of keys) {\n\t\t\tconst l = leftLookup[key];\n\t\t\tconst r = rightLookup[key];\n\t\t\tyield selectFn(l, r, key);\n\t\t}\n\t}\n\n\tfunction flattenObject(obj) {\n\t\tconst result = {};\n\t\tfor (const [k, v] of Object.entries(obj)) {\n\t\t\tif (typeof v === \"object\" &amp;&amp; v) {\n\t\t\t\tfor (const [x, y] of Object.entries(flattenObject(v))) {\n\t\t\t\t\tresult[k + \".\" + x] = y;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tresult[k] = v;\n\t\t}\n\t\treturn result;\n\t}\n\n\tconst mapkv = x => ({key: x[0], value: x[1]});\n\tconst left = Object.entries(flattenObject(nl)).map(mapkv);\n\tconst right = Object.entries(flattenObject(en)).map(mapkv);\n\tconst changes = Array.from(fullOuterJoin(left, right, a => a.key, (l, r, k) => ({ l, r, key: k }))).filter(x => !x.l || !x.r);\n\tconsole.table(changes.map(x => ({ key: x.key, left: x.l?.value, right: x.r?.value })));\n})();<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const getNew = (oldList: any[], newList: any[], equals: Function) => newList.filter(i => !oldList.some(o => equals(o, i)));\nconst getChanged = (oldList: any[], newList: any[], equals: Function, changed: Function) => {\n    return newList.reduce((arr, curr) => {\n        const old = oldList.filter(o => equals(o, curr))[0];\n        if (old &amp;&amp; changed(curr, old)) {\n            arr.push({ key: curr.key, oldvalue: old.value, value: curr.value });\n        }\n        return arr;\n    }, []);\n};\nconst getUnchanged = (oldList: any[], newList: any[], equals: Function, changed: Function) => {\n    return newList.reduce((arr, curr) => {\n        const old = oldList.filter(o => equals(o, curr))[0];\n        if (!(old &amp;&amp; changed(curr, old))) {\n            arr.push({ key: curr.key, value: curr.value });\n        }\n        return arr;\n    }, []);\n};\n\/\/ deleted items is just the lists reversed\nconst getDeleted = (oldList: any[], newList: any[], equals) => getNew(newList, oldList, equals);\n\nconst flattenObject = (obj: object): any => {\n    const result = {};\n    for (const [k, v] of Object.entries(obj)) {\n        if (typeof v === \"object\" &amp;&amp; v) {\n            const flatObject = flattenObject(v);\n            for (const [x, y] of Object.entries(flatObject)) {\n                result[k + \".\" + x] = y;\n            }\n        } else {\n            result[k] = v;\n        }\n    }\n    return result;\n};\n\nconst mapkv = x => ({key: x[0], value: x[1]});\nconst currentJsonValues = JSON.parse(currentVersion.entityValueJson);\nconst previousJsonValues = JSON.parse(previousVersion.entityValueJson);\nconst currentEntries = Object.entries(flattenObject(currentJsonValues)).map(mapkv);\nconst previousEntries = Object.entries(flattenObject(previousJsonValues)).map(mapkv);\n\nconst equal = (a, b) => a.key === b.key;\nconst changed = (a, b) => a.value !== b.value;\n\nconst addedProps = getNew(previousEntries, currentEntries, equal);\nconst changedProps = getChanged(previousEntries, currentEntries, equal, changed);\nconst deletedProps = getDeleted(previousEntries, currentEntries, equal);\nconst unchangedProps = getUnchanged(previousEntries, currentEntries, equal, changed);<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Compare JSON object properties for new\/changed\/deleted<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2188","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=2188"}],"version-history":[{"count":5,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2188\/revisions"}],"predecessor-version":[{"id":4614,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2188\/revisions\/4614"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=2188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=2188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=2188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}