Array Comparison 1

JavaScript performance comparison

Revision 18 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(),f)
        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 (curr,prev) {
             var weight = {
                "[object Undefined]":6,        
                "[object Object]":5,
                "[object Null]":4,
                "[object String]":3,
                "[object Number]":2,
                "[object Boolean]":1
            }
            if (prev) { //mark the objects
                for (var i = prev.length,j,t;i>0;i--) {
                    t = typeof (j = prev[i]);
                    if (j != null && t === "object") {
                         j._pos = i;  
                    } else if (t !== "object" && t != "undefined" ) break;
                }
            }
       
            curr.sort (sorter);
           
            if (prev) {
                for (var k = prev.length,l,t;k>0;k--) {
                    t = typeof (l = prev[k]);
                    if (t === "object" && l != null) {
                        delete l._pos;
                    } else if (t !== "object" && t != "undefined" ) break;
                }
            }
            return curr;
       
            function sorter (a,b) {
       
                 var tStr = Object.prototype.toString
                 var types = [tStr.call(a),tStr.call(b)]
                 var ret = [0,0];
                 if (types[0] === types[1] && types[0] === "[object Object]") {
                     if (prev) return a._pos - b._pos
                     else {
                         return a === b ? 0 : 1;
                     }
                 } else if (types [0] !== types [1]){
                         return weight[types[0]] - weight[types[1]]
                 }
                 
       
               
                return a>b?1:a<b?-1:0;
            }
       
        }
   
   
    });
   
    function sameElementsGlutamat(c, d , referenceCheck) {
      return c.equals(d, referenceCheck);
    }
   
    var sameElementsGlutamat2 = (function () {
        var f, of, objectFlagName;
        of = objectFlagName = "__pos";
        var tstr = function (o) {
            var t = typeof o;
            if (o === null)
                t = "null";
            return t
        };
        var types = {};
        (function () {
            var tmp = {};
            Object.defineProperty(types, tstr(1), {
                set: function (v) {
                    if (f)
                        tmp[v] = -~tmp[v];
                    else
                        tmp[v] = ~-tmp[v];
                },
                get: function () {
                    var ret = 1;
                    for (var k in tmp) {
                        ret &= !tmp[k];
                    }
                    tmp = {};
                    return ret;
                }
            });
        })();
        (function () {
            var tmp = {};
            Object.defineProperty(types, tstr(""), {
   
                set: function (v) {
                    if (f) {
                        tmp[v] = -~tmp[v];
                    } else {
   
                        tmp[v] = ~-tmp[v];
                    }
                },
                get: function () {
                    var ret = 1;
                    for (var k in tmp) {
                        ret &= !tmp[k];
                    }
                    tmp = {};                
                    return ret;
                }
            });
        })();
   
        (function () {
            var tmp = [];
            function add (v) {
                tmp.push(v);
                if (v[of]===undefined) {
                    v[of] = [tmp.length -1];
                } else {
                    v[of].push(tmp.length -1)
                }            
               
            }
            Object.defineProperty(types, tstr({}), {
                get: function () {
                    var ret = true;
                    for (var i = tmp.length - 1; i >= 0; i--) {
                        var c = tmp[i]
                        if (typeof c !== "undefined") {
                            ret = false
                            delete c[of]
                        }
                    }
                    tmp = [];
                    return ret;
                },
                set: function (v) {
                    var pos;
                    if (f) {
                        add (v);
                    } else if (!f && (pos = v[of]) !== void 0) {
                           tmp[pos.pop()] = undefined;
                           if (pos.length === 0)
                                delete v[of];
                    } else {
                            add (v);
                    }
                }
            });
        }());
        (function () {
            var tmp = 0;
            Object.defineProperty(types, tstr(undefined), {
                get: function () {
                    var ret = !tmp;
                    tmp = 0;
                    return ret;
                   
                },
                set: function () {
                    tmp += f ? 1 : -1;
                }
            });
        })();
        (function () {
            var tmp = 0;
            Object.defineProperty(types, tstr(null), {
                get: function () {
                    var ret = !tmp;
                    tmp = 0;
                    return ret;
                },
                set: function () {
                    tmp += f ? 1 : -1;
                }
            });
        })();
         
        var tIt = [tstr(1), tstr(""), tstr({}), tstr(undefined), tstr(null)];
   
        return function eq(a, b) {
           
            f = true;
            for (var i = a.length - 1; i >= 0; i--) {
                var v = a[i];
                types[tstr(v)] = v;
            }
            f = false;
            for (var k = b.length - 1; k >= 0; k--) {
                var w = b[k];
                types[tstr(w)] = w;
            }
            var r = 1;
            for (var l = 0, j; j = tIt[l]; l++) {
                r &= types [j]
            }
   
            return !!r;
        }
    })()
   
        function sameElementsNumberGuy(a, b) {
            if (a.length !== b.length) {
                return false;
            }
            // random trailing hex digits in the hopes of avoiding name collisions
            var objectCountProperty = 'counta2771346634949dec4c5';
            var stringCounts = {};
            var otherCounts = {};
            for (var i = 0; i < a.length; i++) {
                var item = a[i];
                if (Object(item) === item) {
                    item[objectCountProperty] |= 0;
                    item[objectCountProperty]++;
                } else {
                    var counter = (typeof item === 'string' ? stringCounts :
                            otherCounts);
                    counter.hasOwnProperty(item) ? (counter[item]++) :
                            (counter[item] = 1);
                }
            }
            var same = true;
            for (var i = 0; i < b.length; i++) {
                var item = a[i];
                if (Object(item) === item) {
                    if (item[objectCountProperty]) {
                        item[objectCountProperty]--;
                    } else {
                        same = false;
                        break;
                    }
                } else {
                    var counter = (typeof item === 'string' ? stringCounts :
                            otherCounts);
                    if (counter.hasOwnProperty(item) && counter[item]) {
                        counter[item]--;
                    } else {
                        same = false;
                        break;
                    }
                }
            }
            for (var i = 0; i < a.length; i++) {
                delete a[i][objectCountProperty];
            }
            return same;
        }
};
</script>

Test runner

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

Java applet disabled.

Testing in unknown unknown
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…
Glutamat 2
sameElementsGlutamat2 (a,b)
pending…
NumberGuy
sameElementsNumberGuy(a,b)
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment