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…

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.

0 Comments