Array Comparison 1

JavaScript performance comparison

Revision 15 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);
    }
};
</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…

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