Array of object deep comparison with lodash
I prefer pure JS since i haven't got the patience to learn underscore or lodash. So i invent something i have been long dreaming of. The Object.prototype.compare()
. The v0.0.2 is doing only shallow comparison though but adequate for this question.
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
var obj1 = {a:1,b:2,c:3},
obj2 = {c:3,a:1,b:2},
obj3 = {b:2,c:3,a:7};
document.write ("<pre>" + obj1.compare(obj2) + "</pre>\n");
document.write ("<pre>" + obj2.compare(obj3) + "</pre>\n");
document.write ("<pre>" + new Object({a:1, b:2, c:3}).compare({c:3,b:2,a:1,d:0}) + "</pre>\n");
Cool... So then lets continue with the question... I guess... since we already have an Object.prototype.compare()
there should be absolutely no harm in the invention of Array.prototype.compare()
. Lets make it more clever this time. It shall tell primitives from objects. One other thing is, arrays are ordered; so in my book [1,2]
is not equal to [2,1]
. Also this makes the job simpler.
Object.prototype.compare = function(o){
var ok = Object.keys(this);
return typeof o === "object" && ok.length === Object.keys(o).length ? ok.every(k => this[k] === o[k]) : false;
};
Array.prototype.compare = function(a){
return this.every((e,i) => typeof a[i] === "object" ? a[i].compare(e) : a[i] === e);
}
var x = [{a:1, b:2}, {c:3, d:4}],
y = [{b:2, a:1}, {d:4, c:3}],
a = [1,2,3,4,5],
b = [1,2,3,4,5],
p = "fourtytwo",
r = "thirtyseven",
n = 42,
m = 37;
document.writeln(x.compare(y)); // the question is answered here
document.writeln(a.compare(b));
document.writeln(p.compare(r)); // these primitives end up at Object prototype
document.writeln(n.compare(m)); // so modify Object.prototype.compare () accordingly
You can make use of differenceWith() with an isEqual() comparator, and invoke isEmpty to check if they are equal or not.
var isArrayEqual = function(x, y) {
return _(x).differenceWith(y, _.isEqual).isEmpty();
};
var result1 = isArrayEqual(
[{a:1, b:2}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
var result2 = isArrayEqual(
[{a:1, b:2, c: 1}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
document.write([
'<div><label>result1: ', result1, '</label></div>',
'<div><label>result2: ', result2, '</label></div>',
].join(''));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js"></script>
UPDATE June 22, 2018
This update is in response to the comment below:
@ryeballar if any of the array is undefined it returns true. How would you solve this. Thanks in advance buddy
As stated in the differenceWith
documentation:
The order and references of result values are determined by the first array.
This means that as long as all the items in the first array will match everything else in the second array, then the resulting array from the differenceWith
invocation will be empty.
An alternative solution that truly solves the problem is to use xorWith()
with the same chain of functions from the solution above.
var isArrayEqual = function(x, y) {
return _(x).xorWith(y, _.isEqual).isEmpty();
};
var result1 = isArrayEqual(
[{a:1, b:2}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
var result2 = isArrayEqual(
[{a:1, b:2, c: 1}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}]
);
var result3 = isArrayEqual(
[{a:1, b:2, c: 1}, {c:3, d:4}],
[{b:2, a:1}, {d:4, c:3}, undefined]
);
console.log('result1:', result1);
console.log('result2:', result2);
console.log('result3:', result3);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Both answers on above with xorWith
& differenceWith
do not take in count if the array has different length only if the current item is found in the second array.
var isArrayEqual = function(x, y) {
return _(x).xorWith(y, _.isEqual).isEmpty();
};
var result = isArrayEqual(
[{a:1, b:2}],
[{a:1, b:2}, {a:1, b:2}]
);
console.log('result should be false:', result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
In that particular case, we also would have to compare both arrays length.
const isArrayEqual = function(x, y) {
const isSameSize = _.size(x) === _.size(y);
return isSameSize && _(x).xorWith(y, _.isEqual).isEmpty();
};
const result = isArrayEqual(
[{a:1, b:2}],
[{a:1, b:2}, {a:1, b:2}]
);
console.log('result should be false:', result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Following @ryeballar answer, if you only want to import specific lodash methods you can use this notation:
import { isEmpty, isEqual, xorWith } from 'lodash';
export const isArrayEqual = (x, y) => isEmpty(xorWith(x, y, isEqual));