├── .npmignore ├── .gitignore ├── index.js ├── package.json ├── test.js └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | test.js 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | strict.custom = custom; 4 | 5 | module.exports = strict; 6 | 7 | function strict(){ 8 | return diff(null, [].slice.call(arguments, 0)); 9 | } 10 | 11 | function custom( opts ){ 12 | return diff(opts, [].slice.call(arguments, 1)); 13 | } 14 | 15 | function diff( opts, subjects ){ 16 | var length = subjects.length; 17 | var ref = subjects[0]; 18 | var diff = {}; 19 | var equal = opts && opts.equal || isStrictEqual; 20 | var c; 21 | var keys; 22 | var keysLength; 23 | var key; 24 | var u; 25 | 26 | for (var i = 1;i < length;i++) { 27 | c = subjects[i]; 28 | keys = Object.keys(c); 29 | keysLength = keys.length; 30 | 31 | for (u = 0;u < keysLength;u++) { 32 | key = keys[u]; 33 | 34 | if (!equal(c[key], ref[key])) 35 | diff[key] = c[key]; 36 | } 37 | } 38 | 39 | return diff; 40 | } 41 | 42 | function isStrictEqual( a, b ){ 43 | return a === b; 44 | } 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object-diff", 3 | "description": "Get the diff from objectA to objectB", 4 | "version": "0.0.4", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/srcagency/object-diff.git" 9 | }, 10 | "keywords": [ 11 | "object", 12 | "diff", 13 | "patch" 14 | ], 15 | "author": "Thomas Jensen (http://src.agency)", 16 | "devDependencies": { 17 | "tape": "^4.5.1", 18 | "testling": "^1.7.1" 19 | }, 20 | "testling": { 21 | "files": "test.js", 22 | "browsers": [ 23 | "ie/8..latest", 24 | "chrome/22..latest", 25 | "firefox/16..latest", 26 | "safari/6..latest", 27 | "opera/11.0..latest", 28 | "iphone/6..latest", 29 | "ipad/6..latest", 30 | "android-browser/latest" 31 | ] 32 | }, 33 | "scripts": { 34 | "test": "node test", 35 | "browser-test": "testling" 36 | }, 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var diff = require('./'); 4 | var test = require('tape'); 5 | 6 | test(function( t ) { 7 | var a = { 8 | speed: 4, 9 | power: 54, 10 | height: undefined, 11 | level: 1, 12 | }; 13 | 14 | var b = { 15 | speed: 4, // unchanged 16 | power: 22, // changed 17 | level: undefined, // changed 18 | weight: 10, // added 19 | }; 20 | 21 | t.deepEqual(diff(a, b), { 22 | power: 22, 23 | level: undefined, 24 | weight: 10, 25 | }); 26 | 27 | var c = { 28 | speed: 5, // changed 29 | power: 54, // unchanged 30 | level: 100, // changed 31 | material: 'steel', // added 32 | location: undefined, // added but undefined 33 | }; 34 | 35 | t.deepEqual(diff(a, b, c), { 36 | speed: 5, 37 | power: 22, 38 | level: 100, 39 | weight: 10, 40 | material: 'steel', 41 | }); 42 | 43 | t.deepEqual(diff({}, {}), {}); 44 | 45 | t.end(); 46 | }); 47 | 48 | test('Custom equality', function( t ) { 49 | var created = '2016-04-24T10:39:23.419Z'; 50 | var now = new Date(); 51 | 52 | var a = { 53 | created: new Date(created), 54 | updated: new Date(created), 55 | }; 56 | 57 | var b = { 58 | created: new Date(created), // unchanged 59 | updated: now, // changed 60 | }; 61 | 62 | t.deepEqual(diff(a, b), { 63 | created: new Date(created), 64 | updated: now, 65 | }, 'expected default behavior'); 66 | 67 | t.deepEqual(diff.custom({ 68 | equal: dateAwareComparator, 69 | }, a, b), { 70 | updated: now, 71 | }); 72 | 73 | t.end(); 74 | }); 75 | 76 | function dateAwareComparator( a, b ){ 77 | if (a instanceof Date && b instanceof Date) 78 | return a.getTime() === b.getTime(); 79 | 80 | return a === b; 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # object diff 2 | 3 | Get the minimal patch to extend objectA with to transform it into objectB 4 | 5 | [![npm version][npm-image]][npm-url] 6 | 7 | Consider an object retrieved from a server: 8 | 9 | ```js 10 | { 11 | name: 'Peter', 12 | age: 26, 13 | height: 187, 14 | } 15 | ``` 16 | 17 | Now the user changes stuff using some frontend (e.g. a HTML form) and ends 18 | with: 19 | 20 | ```js 21 | { 22 | name: 'Peter', 23 | age: 27, 24 | height: 186, 25 | } 26 | ``` 27 | 28 | When he hits save, you only want to send off the changed parts to the servers, 29 | to save bits (because you're indeed a programmer), but also to avoid any 30 | unnecessary "merge conflicts" at the server. 31 | 32 | Imagine two users changing the same object; if they did not change the exact 33 | same keys of the object, the last user won't erase the first user's changes - 34 | in a lot of cases, that's the expected behavior. 35 | 36 | ## Install 37 | 38 | ``` 39 | npm install object-diff 40 | ``` 41 | 42 | ## Usage 43 | 44 | ```js 45 | var diff = require('object-diff'); 46 | 47 | var a = { 48 | speed: 4, 49 | power: 54, 50 | height: undefined, 51 | level: 1, 52 | }; 53 | 54 | var b = { 55 | speed: 4, // unchanged 56 | power: 22, // changed 57 | level: undefined, // changed 58 | weight: 10, // added 59 | }; 60 | 61 | diff(a, b); 62 | /* 63 | { 64 | power: 22, 65 | level: undefined, 66 | weight: 10, 67 | } 68 | */ 69 | 70 | 71 | // using a custom equality function 72 | 73 | var past = '2016-04-24T10:39:23.419Z'; 74 | 75 | diff.custom({ 76 | equal: dateAwareComparator, 77 | }, { 78 | then: new Date(past), 79 | }, { 80 | then: new Date(past), 81 | }); 82 | /* 83 | {} 84 | */ 85 | 86 | function dateAwareComparator( a, b ){ 87 | if (a instanceof Date && b instanceof Date) 88 | return a.getTime() === b.getTime(); 89 | 90 | return a === b; 91 | } 92 | 93 | ``` 94 | 95 | ## License 96 | 97 | [MIT](http://opensource.org/licenses/MIT) © 98 | [src.agency](http://src.agency) / Thomas Jensen 99 | [npm-image]: https://img.shields.io/npm/v/object-diff.svg?style=flat 100 | [npm-url]: https://npmjs.org/package/object-diff 101 | --------------------------------------------------------------------------------