Skip to content
This repository has been archived by the owner on Mar 20, 2022. It is now read-only.

Include an implementation of isEqual and isObject #22

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,6 @@ AppDispatcher.register((payload) => {
});
```

## Dependencies

* `lodash` for `isObject` and `isEqual`

## Running Tests

```
Expand Down
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,5 @@
"mocha": "^2.2.5",
"rimraf": "^2.4.2",
"webpack": "^1.10.5"
},
"dependencies": {
"lodash": "^3.10.0"
}
}
2 changes: 1 addition & 1 deletion src/ArraySchema.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import isObject from 'lodash/lang/isObject';
import { isObject } from './utils';

export default class ArraySchema {
constructor(itemSchema) {
Expand Down
3 changes: 1 addition & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import EntitySchema from './EntitySchema';
import ArraySchema from './ArraySchema';
import isObject from 'lodash/lang/isObject';
import isEqual from 'lodash/lang/isEqual';
import { isObject, isEqual } from './utils';

function defaultAssignEntity(normalized, key, entity) {
normalized[key] = entity;
Expand Down
56 changes: 56 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
function baseIsEqual(value, other, references) {
let indexOfValue = references.indexOf(value),
indexOfOther = references.indexOf(other);

if (isObject(value) && indexOfValue === -1) {
references.push(value);
}
if (isObject(value) && indexOfOther === -1) {
references.push(other);
}

if (value === other) {
return true;
} else if (indexOfValue >= 0 || indexOfOther >= 0) {
return false;
}

if (isObject(value) && isObject(other)) {
let returnValue = false;

if (value.constructor.name === 'Array' && other.constructor.name === 'Array') {
if (value.length === other.length) {
let equalSoFar = true;
for (let i = 0; i < value.length; i++) {
equalSoFar = equalSoFar && baseIsEqual(value[i], other[i], references);
}
returnValue = equalSoFar;
}
} else {
let valueKeys = Object.keys(value),
otherKeys = Object.keys(other);

if (baseIsEqual(valueKeys, otherKeys, [])) {
returnValue = valueKeys.reduce(function(memo, prop){

return memo && baseIsEqual(value[prop], other[prop], references);
}, true);
}
}
references.pop();
references.pop();

return returnValue;
}

return false;
}

export function isObject(value) {
const type = typeof value;
return value && (type === 'object' || type === 'function');
}

export function isEqual(value, other) {
return baseIsEqual(value, other, []);
}
269 changes: 269 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
'use strict';

var should = require('chai').should(),
utils = require('../src/utils'),
isEqual = utils.isEqual;

describe('isEqual', function() {
let value, other;

describe ('two numbers', function(){
it('returns true for equal numbers', function(){
isEqual(8, 8).should.equal(true);
});

it('returns false for different numbers', function(){
isEqual(8, 64).should.equal(false);
});
});

describe ('one number, one object', function(){
it('returns false', function(){
isEqual(8, {}).should.equal(false);
isEqual({}, 16).should.equal(false);
});
});

describe ('one function, one object', function(){
it('returns false', function(){
value = function(){};
other = { key: undefined };
isEqual(value, other).should.equal(false);
});
});

describe ('null and undefined', function(){
it('returns false', function(){
isEqual(null, undefined).should.equal(false);
});
});

describe('two object trees, one level deep', function(){
it('returns true for objects with the same key/value pairs', function(){
value = { one: 1 };
other = { one: 1 };
isEqual(value, other).should.equal(true);

value = { one: 1, fib: [0, 1, 1, 2, 3, 5, 8] };
other = { one: 1, fib: [0, 1, 1, 2, 3, 5, 8] };
isEqual(value, other).should.equal(true);

value = { one: null };
other = { one: null };
isEqual(value, other).should.equal(true);
});

it('returns false for objects with different key/value pairs', function(){
value = { one: 1 };
other = { two: 1 };
isEqual(value, other).should.equal(false);

value = { one: 1 };
other = { one: 1, two: 2 };
isEqual(value, other).should.equal(false);
});
});

describe('two object trees, nested levels', function(){
it('returns true for deeply equal objects', function(){
value = {
'nested1': {
'nested11': 'n',
'nested12': 12,
'nested13': [
'131',
'132',
{
'nested13_0': 130
}
]
},
'nested2': {
}
}, other = {
'nested1': {
'nested11': 'n',
'nested12': 12,
'nested13': [
'131',
'132',
{
'nested13_0': 130
}
]
},
'nested2': {
}
};
isEqual(value, other).should.equal(true);

value = {
'nested1': [
1,
1
],
'nested2': [
{
'nested2_0': 1
}
]
}, other = {
'nested1': [
1,
1
],
'nested2': [
{
'nested2_0': 1
}
]
};
isEqual(value, other).should.equal(true);
});

it('returns false for objects with the slightest difference', function(){
value = {
'nested1': {
'nested11': 'n',
'nested12': 12,
'nested13': [
'131',
'132',
{
'nested13_0': 130
}
]
},
'nested2': {
}
}, other = {
'nested1': {
'nested11': 'n',
'nested12': 12,
'nested13': [
'131',
'132',
{
'nested13_0': 131
}
]
},
'nested2': {
}
};
isEqual(value, other).should.equal(false);
});

it('returns true for objects with circular references', function(){
value = {
'nested1': {
'nested13': [
'131',
{
'nested13_0': 130
}
]
}
}, other = {
'nested1': {
'nested13': [
'131',
{
'nested13_0': 130
}
]
}
};
value.nested1.nested14 = value;
other.nested1.nested14 = value;
isEqual(value, other).should.equal(true);

let somethingNested = { nestedKey: 'isNested' };
somethingNested.self = somethingNested;
value = {
'nested1': {
'nested11': somethingNested,
'nested12': somethingNested,
'nested13': {
'nested131': somethingNested,
'nested132': somethingNested,
'nested133': [
somethingNested,
somethingNested,
{
'deep': somethingNested
}
]
}
}
}, other = {
'nested1': {
'nested11': somethingNested,
'nested12': somethingNested,
'nested13': {
'nested131': somethingNested,
'nested132': somethingNested,
'nested133': [
somethingNested,
somethingNested,
{
'deep': somethingNested
}
]
}
}
};
isEqual(value, other).should.equal(true);
});

it('detects circular references and returns false before falling in an infinite loop', function(){
value = {
'nested1': {
'nested13': [
'131',
{
'nested13_0': 130
}
]
},
}, other = {
'nested1': {
'nested13': [
'131',
{
'nested13_0': 130
}
]
},
};
value.nested1.nested14 = value;
other.nested1.nested14 = other;
isEqual(value, other).should.equal(false);

let somethingNested = { nestedKey: 'isNested' };
somethingNested.self = somethingNested;
value = {
'nested1': {
'nested11': somethingNested,
'nested12': somethingNested,
'nested13': {
'nested131': somethingNested,
'nested132': somethingNested
}
}
}, other = {
'nested1': {
'nested11': somethingNested,
'nested12': somethingNested,
'nested13': {
'nested131': somethingNested,
'nested132': {
'nested1321': somethingNested
}
}
}
};
isEqual(value, other).should.equal(false);
});
});
});