array-unique icon indicating copy to clipboard operation
array-unique copied to clipboard

`new Set(arr)` seems faster

Open jimmywarting opened this issue 5 years ago • 0 comments
trafficstars

  • There where no benchmarks to be found.
  • installing and executing the function didn't show any numbers (#7)
  • updating benchmarked and executing the test was alarming too me cuz i wasn't sure if the mutable function would change the fixture (cuz i didn't get an array in my test function)
  • so i conducted my own test to se how this compared to using native new Set(iterable)
Vanilla source code (benchmark)
/**
 * Source: http://jsperf.com/array-unique2/15
 */

function randomize(n) {
  var values = [];
  var rand;
  while (n--) {
    rand = Math.random() * n * 2 | 0;
    if (rand < values.length) {
      values.push(values[rand]);
    }
    else {
      switch (Math.random() * 3 | 0) {
      case 0:
        values.push(Math.random() * 1e10 | 0);
        break;
      case 1:
        values.push((Math.random() * 1e32).toString(36));
        break;
      case 2:
        values.push({});
        break;
      }
    }
  }

  return values;
};

function unique(arr) {
  var len = arr.length;
  var i = -1;

  while (i++ < len) {
    var j = i + 1;

    for (; j < arr.length; ++j) {
      if (arr[i] === arr[j]) {
        arr.splice(j--, 1);
      }
    }
  }
  return arr;
};

function uniqueImmutable(arr) {
  var arrLen = arr.length;
  var newArr = new Array(arrLen);

  for (var i = 0; i < arrLen; i++) {
    newArr[i] = arr[i];
  }

  return unique(newArr);
};

function set(arr) {
  return [...new Set(arr)];
}

function setWithoutCasting(arr) {
  return new Set(arr);
}

for (let arr of [randomize(10000), randomize(500), randomize(10)]) {
  console.group(`Array(${arr.length})`)
  for (let fn of [unique, uniqueImmutable, set, setWithoutCasting]) {  
    let runs = 0
    let startCountingAt = performance.now() + 3000 // 3 sec later
    let stopAt = performance.now() + 5000 // 5 sec later
    let now = performance.now()
    while (now < stopAt) {
      // Pre run it a few times
      if (now > startCountingAt) {
        runs++;
      }
      fn([...arr]) // copy the array for each test and fn (unique mutates)
      now = performance.now();
    }
    console.log(`executed "${fn.name}(arr)" ${runs} times`)
  }
  console.groupEnd(`Array(${arr.length})`)
}
it's ashamed that ppl expect to be working with arrays all the times. `Set` is perfect for when you need uniqueness and it dose not have to be an Array all the time...

you can loop over Set the exact same way as with an Array, both Set and Array's have forEach and for..of loop support.

According to my benchmarks Set seems to be the way to go, it runs faster than your unique function in FF, Blink, Safari and NodeJS.

Need some numbers?

(higher number = faster)

Chrome v84

Skärmavbild 2020-08-07 kl  02 04 43

Safari v14 Skärmavbild 2020-08-07 kl  02 11 08

node v14.2.0 Skärmavbild 2020-08-07 kl  02 11 43

new Set(iterable) is so small, don't need any 3th party modules, it's built in and dose not offer readability for performance that i think this should be the recommended way for all who seeks for a solution

jimmywarting avatar Aug 07 '20 00:08 jimmywarting