# Array Comparison 1

## JavaScript performance comparison

Revision 12 of this test case created by C5H8NNaO4

## Preparation code

``````

<script>
Benchmark.prototype.setup = function() {
var a = [];
var b = [];
var obj;
var arr;
var max = 10000;

for (var i = 0; i < max; i++) {
if (i % 5) {
arr = [1, 2];
b[max - i - 1] = arr;
a[i] = arr;
} else if (i % 2) {
obj = {
"hello": "world"
};
b[max - i - 1] = obj;
a[i] = obj;
} else {
b[max - i - 1] = i % 100;
a[i] = i % 100;
}
}

function sameElementsBrunoLM(a, b) {
// immediately discard if they are of different sizes
if (a.length != b.length) return false;

b = b.slice(0); // clone to keep original values after the function
a.forEach(function(e) {
var i;
if ((i = b.indexOf(e)) != -1) b.splice(i, 1);
});

return !b.length;
}

function sameElementsBruno(a, b) {

// if length is not the same then must not be equal
if (a.length != b.length) return false;

// do an initial sort which will group types
a.sort();
b.sort();

for (var i = 0; i < a.length; i++) {

// if primitive then comparison will do
if (isPrimitive(a[i]) && isPrimitive(b[i])) {

if (a[i] != b[i]) return false;
}
// if not primitive then we will need to check for obj ref
else if (!isPrimitive(a[i]) && !isPrimitive(b[i])) {
if (!a[i].__aCount) a[i].__aCount = 0;
if (!b[i].__bCount) b[i].__bCount = 0;
a[i].__aCount = a[i].__aCount + 1;
b[i].__bCount = b[i].__bCount + 1;
}
// if both types are not the same then this array
// contains different number of primitives
else {
return false;
}

}
for (var i = 0; i < a.length; i++) {
if (!isPrimitive(a[i])) {
if (!a[i].__aCount) {
return false;
}
if (!a[i].__bCount) {
return false;
}
if (a[i].__aCount != a[i].__bCount) {
return false;
}
}
}
// if it gets this far it must be the same
return true;
}

function isPrimitive(arg) {
var type = typeof arg;
return arg == null || (type != "object" && type != "function");
}

function sameElementsHamidi(a, b) {
if (a.length != b.length) {
return false;
}
var ourB = b.concat();
return a.every(function(item) {
var index = ourB.indexOf(item);
if (index < 0) {
return false;
} else {
ourB.splice(index, 1);
return true;
}
});
}

function sameElementsBergi(a) {
var map, maps = [],

// counting booleans, numbers and strings
nulls = [],

// counting undefined and null
nans = [],

// counting nans
objs, counts, objects = [],
al = arguments.length;
// quick escapes:
if (al < 2) return true;
var l0 = a.length;
if ([].slice.call(arguments).some(function(s) {
return s.length != l0;
})) return false;

for (var i = 0; i < al; i++) {
var multiset = arguments[i];
maps.push(map = {}); // better: Object.create(null);
objects.push({
vals: objs = [],
count: counts = []
});
nulls[i] = 0;
nans[i] = 0;
for (var j = 0; j < l0; j++) {
var val = multiset[j];
if (val !== val) nans[i]++;
else if (val === null) nulls[i]++;
else if (Object(val) === val) { // non-primitive
var ind = objs.indexOf(val);
if (ind > -1) counts[ind]++;
else objs.push(val), counts.push(1);
} else { // booleans, strings and numbers do compare together
if (typeof val == "boolean") val = +val;
if (val in map) map[val]++;
else map[val] = 1;
}
}
}

// testing if nulls and nans are the same everywhere
for (var i = 1; i < al; i++)
if (nulls[i] != nulls[0] || nans[i] != nans[0]) return false;

// testing if primitives were the same everywhere
var map0 = maps[0];
for (var el in map0)
for (var i = 1; i < al; i++) {
if (map0[el] !== maps[i][el]) return false;
delete maps[i][el];
}
for (var i = 1; i < al; i++)
for (var el in maps[i])
return false;

// testing of objects were the same everywhere
var objs0 = objects[0].vals,
ol = objs0.length;
counts0 = objects[0].count;
for (var i = 1; i < al; i++)
if (objects[i].count.length != ol) return false;
for (var i = 0; i < ol; i++)
for (var j = 1; j < al; j++)
if (objects[j].count[objects[j].vals.indexOf(objs0[i])] != counts0[i]) return false;

// else, the multisets are equal:
return true;
}

function mapTypes(a) {

var _map = {};

for (var i = 0; i < a.length; i++) {

var _t = toType(a[i]);
if (keyGen.hasOwnProperty(_t)) {
_t = _t + keyGen[_t](a[i]);
}

if (!_map[_t]) {
_map[_t] = [];
}

_map[_t].push(i);
}

return _map;
}

function mapTypesMatch(mapA, mapB) {
var a = Object.keys(mapA);
var b = Object.keys(mapB);

if (a.length != b.length) return false;

for (var i = 0; i < a.length; i++) {

if (!mapB[a[i]]) return false;
if (mapA[a[i]].length != mapB[a[i]].length) return false;
}

return true;
}

function arrayElementsMatch(mapA, sourceA, mapB, sourceB) {

for (var key in mapA) {
if (mapA.hasOwnProperty(key)) {

for (var i = 0; i < mapA[key].length; i++) {
var found = false;
for (var k = 0; k < mapB[key].length; k++) {
if (sourceA[mapA[key][i]] === sourceB[mapB[key][k]]) {
mapB[key].slice(k, 1);
found = true;
break;
}
}
if (!found) return false;
}
}
}

return true;
}

var keyGen = {
"[object Object]": function(obj) {
return Object.keys(obj).length;
},
"[object Array]": function(arr) {
return arr.length;
},
"[object Date]": function(d) {
return d.getYear();
},
"[object Number]": function(n) {
return n.toString().length;
},
"[object String]": function(str) {
return str.length;
}
}

var toType = function(obj) {
return Object.prototype.toString.call(obj);
}

function sameElementsBruno2(a, b) {

if (a.length != b.length) return false;

var mapA = mapTypes(a);
var mapB = mapTypes(b);

if (!mapTypesMatch(mapA, mapB)) return false;
if (!arrayElementsMatch(mapA, a, mapB, b)) return false;

return true;
}

function sameElementsBruno3(a, b) {
var objs = [];
// if length is not the same then must not be equal
if (a.length != b.length) return false;

// do an initial sort which will group types
a.sort();
b.sort();

for (var i = 0; i < a.length; i++) {

var aIsPrimitive = isPrimitive(a[i]);
var bIsPrimitive = isPrimitive(b[i]);

// NaN will not equal itself
if (a[i] !== a[i]) {
if (b[i] === b[i]) {
return false;
}
} else if (aIsPrimitive && bIsPrimitive) {

if (a[i] != b[i]) return false;
}
// if not primitive increment the __count property
else if (!aIsPrimitive && !bIsPrimitive) {
incrementCountA(a[i]);
incrementCountB(b[i]);
// keep track on non-primitive objects
objs.push(i);
}
// if both types are not the same then this array
// contains different number of primitives
else {
return false;
}

}

var result = true;

for (var i = 0; i < objs.length; i++) {
var ind = objs[i];
if (a[ind].__aCount !== a[ind].__bCount) result = false;
if (b[ind].__aCount !== b[ind].__bCount) result = false;

// revert object to what it was
// before entering this function
delete a[ind].__aCount;
delete a[ind].__bCount;
delete b[ind].__aCount;
delete b[ind].__bCount;
}

return result;
}

// inspired by @Bergi's code

function isPrimitive(arg) {
return Object(arg) !== arg;
}

function incrementCountA(arg) {
if (arg.hasOwnProperty("__aCount")) {
arg.__aCount = arg.__aCount + 1;
} else {
Object.defineProperty(arg, "__aCount", {
enumerable: false,
value: 1,
writable: true,
configurable: true
});
}
}

function incrementCountB(arg) {
if (arg.hasOwnProperty("__bCount")) {
arg.__bCount = arg.__bCount + 1;
} else {
Object.defineProperty(arg, "__bCount", {
enumerable: false,
value: 1,
writable: true,
configurable: true
});
}
}

function sameElementsTHG(a, b) {
var hash = function(x) {
return typeof x + (typeof x == "object" ? a.indexOf(x) : x);
}

return a.map(hash).sort().join() == b.map(hash).sort().join();
}

Object.defineProperty(Boolean.prototype, "equals", {
enumerable: false,
configurable: true,
value: function(c) {
return this == c;
}
});

Object.defineProperty(Number.prototype, "equals", {
enumerable: false,
configurable: true,
value: Boolean.prototype.equals
});
Object.defineProperty(String.prototype, "equals", {
enumerable: false,
configurable: true,
value: Boolean.prototype.equals
});

Object.defineProperty(Object.prototype, "equals", {
enumerable: false,
configurable: true,
value: function(c, reference) {
if (true === reference) return this === c;
if (typeof this != typeof c) {
return false;
}
var d = [Object.keys(this), Object.keys(c)],
f = d[0].length;
if (f !== d[1].length) {
return false;
}
for (var e = 0; e < f; e++) {
if (d[0][e] != d[1][e] || !this[d[0][e]].equals(c[d[1][e]])) {
return false;
}
}
return true;
}
});
Object.defineProperty(Array.prototype, "equals", {
enumerable: false,
configurable: true,
value: function(c, reference) {

var d = this.length;
if (d != c.length) {
return false;
}
var f = Array.prototype.equals.sort(this.concat());
c = Array.prototype.equals.sort(c.concat())
if (reference) {
for (var e = 0; e < d; e++) {
if (f[e] != c[e]) {
return false;
}
}
} else {
for (var e = 0; e < d; e++) {
if (!f[e].equals(c[e], reference)) {
return false;
}
}
}
return true;

}
});
Object.defineProperty(Array.prototype.equals, "sort", {
enumerable: false,
value: function sort(el) {
var i = 0,
ret;
!sort.el && (sort.el = el);
ret = el.sort(srt);
if (sort.el != el) {
var l = Math.max(el.length, sort.el.length);
for (var j = 0; j < l; j++) {
if (typeof el[j] === "object" && el[j] && el[j]._srt) delete el[j]._srt;
if (typeof sort.el[j] === "object" && sort.el[j] && sort.el[j]._srt) delete sort.el[j]._srt;
}
}
sort.el = el
return ret;

function srt(a, b, c) {
var types = [typeof a, typeof b];
var r = [0, 0];

if (types[0] === "object" && types[1] === "object") {
if (el === sort.el) {
!a._srt && (a._srt = ++i);
!b._srt && (b._srt = ++i);

} else {
a._srt && (r[0] = a._srt)
b._srt && (r[1] = b._srt)
}
return r[0] - r[1];
}
if (types[0] == "object") {
return 1;
}
if (types[1] == "object") {
return -1;
}
return a > b ? 1 : a < b ? -1 : 0;
}
}

});

function sameElementsGlutamat(c, d , referenceCheck) {
return c.equals(d, referenceCheck);
}
};

</script>
``````

## Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
BrunoLM
``sameElementsBrunoLM(a, b);``
pending…
Hamidi
``sameElementsHamidi(a, b)``
pending…
Bergi
``sameElementsBergi(a, b)``
pending…
Bruno
``sameElementsBruno3(a, b);``
pending…
THG
``sameElementsTHG(a, b)``
pending…
Glutamat (deep check)
``sameElementsGlutamat (a,b)``
pending…
Glutamat (reference check)
``sameElementsGlutamat (a,b,true)``
pending…

## Revisions

You can edit these tests or add even more tests to this page by appending `/edit` to the URL.