Skip to content

Commit 24defbb

Browse files
committed
Add basic functionality to diff two objects
1 parent e540a5c commit 24defbb

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import isEqual from 'lodash/fp/isEqual';
2+
3+
const diff = (lhs, rhs) => {
4+
if (isEqual(lhs, rhs)) return {};
5+
6+
if (typeof rhs !== 'object' || rhs === null) return rhs;
7+
8+
return Object.keys(rhs).map(key => {
9+
if (!lhs.hasOwnProperty(key)) return { [key]: rhs[key] };
10+
11+
const lhsValue = lhs[key];
12+
const rhsValue = rhs[key];
13+
if (isEqual(lhsValue, rhsValue)) return {};
14+
15+
return { [key]: diff(lhsValue, rhsValue) };
16+
}).reduce((acc, next) => ({ ...acc, ...next }), {});
17+
};
18+
19+
export default diff;

src/index.spec.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { expect } from 'chai';
2+
3+
import diff from './';
4+
5+
describe('.diff', () => {
6+
7+
describe('base case', () => {
8+
describe('equal', () => {
9+
it('returns empty object when given ints are equal', () => {
10+
expect(diff(1, 1)).to.deep.equal({});
11+
});
12+
13+
it('returns empty object when given strings are equal', () => {
14+
expect(diff('a', 'a')).to.deep.equal({});
15+
});
16+
17+
it('returns empty object when given booleans are equal', () => {
18+
expect(diff(true, true)).to.deep.equal({});
19+
});
20+
21+
it('returns empty object when given parameters are null', () => {
22+
expect(diff(null, null)).to.deep.equal({});
23+
});
24+
25+
it('returns empty object when given parameters are undefined', () => {
26+
expect(diff(undefined, undefined)).to.deep.equal({});
27+
});
28+
29+
it('returns empty object when given objects are equal', () => {
30+
expect(diff({ a: 1 }, { a: 1 })).to.deep.equal({});
31+
});
32+
33+
it('returns empty object when given arrays are equal', () => {
34+
expect(diff([1], [1])).to.deep.equal({});
35+
});
36+
});
37+
38+
describe('not equal and not object', () => {
39+
it('returns right hand side value when given ints are different', () => {
40+
expect(diff(1, 2)).to.deep.equal(2);
41+
});
42+
43+
it('returns right hand side value when given strings are different', () => {
44+
expect(diff('a', 'b')).to.deep.equal('b');
45+
});
46+
47+
it('returns right hand side value when given booleans are different', () => {
48+
expect(diff(true, false)).to.deep.equal(false);
49+
});
50+
51+
it('returns right hand side value when given values are not both null', () => {
52+
expect(diff('hello', null)).to.deep.equal(null);
53+
});
54+
55+
it('returns right hand side value when given values are not both undefined', () => {
56+
expect(diff('hello', undefined)).to.deep.equal(undefined);
57+
});
58+
});
59+
});
60+
61+
describe('recursive case', () => {
62+
describe('object', () => {
63+
it('returns right hand side value when given objects are different', () => {
64+
expect(diff({ a: 1 }, { a: 2 })).to.deep.equal({ a: 2 });
65+
});
66+
67+
it('returns subset of right hand side value when sibling objects differ', () => {
68+
expect(diff({ a: { b: 1 }, c: 2 }, { a: { b: 1 }, c: 3 })).to.deep.equal({ c: 3 });
69+
});
70+
71+
it('returns subset of right hand side value when nested values differ', () => {
72+
expect(diff({ a: { b: 1, c: 2} }, { a: { b: 1, c: 3 } })).to.deep.equal({ a: { c: 3 } });
73+
});
74+
75+
it('returns subset of right hand side value when nested values differ at multiple paths', () => {
76+
expect(diff({ a: { b: 1 }, c: 2, d: { e: 100 } }, { a: { b: 99 }, c: 3, d: { e: 100 } })).to.deep.equal({ a: { b: 99 }, c: 3 });
77+
});
78+
79+
it('returns subset of right hand side value when a key value has been deleted', () => {
80+
expect(diff({ a: { b: 1 }, c: 2, d: { e: 100 } }, { a: { b: 1 }, c: 2, d: {} })).to.deep.equal({ d: {} });
81+
});
82+
83+
it('returns subset of right hand side value when a key value has been added', () => {
84+
expect(diff({ a: 1 }, { a: 1, b: 2 })).to.deep.equal({ b: 2 });
85+
});
86+
});
87+
88+
describe('arrays', () => {
89+
it('returns right hand side value as object of indices to value when arrays are different', () => {
90+
expect(diff([1], [2])).to.deep.equal({ 0: 2 });
91+
});
92+
93+
it('returns subset of right hand side array as object of indices to value when arrays differs at multiple indicies', () => {
94+
expect(diff([1, 2, 3], [9, 8, 3])).to.deep.equal({ 0: 9, 1: 8 });
95+
});
96+
97+
it('returns subset of right hand side array as object of indices to value when right hand side array has deletions', () => {
98+
expect(diff([1, 2, 3], [1, 3])).to.deep.equal({ 1: 3 });
99+
});
100+
101+
it('returns subset of right hand side array as object of indices to value when right hand side array has additions', () => {
102+
expect(diff([1, 2, 3], [1, 2, 3, 9])).to.deep.equal({ 3: 9 });
103+
});
104+
});
105+
});
106+
});

0 commit comments

Comments
 (0)