8

Just saw someone write this:

let id = 1;
...
let employee = null;

for (const e of employees) {
    if (e.id === id) {
        employee = e;
        break;
    }
}

seems like an overcomplicated way of writing this:

let id = 1;
...
let employee = employees.find(e => e.id === id);

Is there any benefit to using a loop with a break vs a find()?

What is find()'s implementation behind the curtain?

Artur Grigio
  • 5,185
  • 8
  • 45
  • 65
  • 2
    just a minor difference, `find` returns `undefined` instead of `null`, if not found. – Nina Scholz Jun 13 '18 at 18:24
  • 1
    Two questions were asked, but I suspect the first one is more interesting. As far as the implementation of `find()`, it is probably a highly-optimized loop in a lower-level language. – MarkHu Aug 18 '18 at 19:05
  • Probably the same. Find is way more succinct and declarative no? – The Dembinski Jun 13 '18 at 18:15

5 Answers5

14

Perfomance

.find() is faster than for...break.

Check this link for test results. for...break is 30% slower than .find()


.find() source code can be found here

.find() is not supported in older browsers like IE11 and below. You need to use a polyfill instead.


Opinion

.find() is better due to complexity level and the internal algorithm used. Using for...break you will always doing a linear search which means n * n repetitions. The bigger the array, the slower the function.

  • For breaking loop, you can also use [some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) or [every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every). Check here https://stackoverflow.com/questions/6260756/how-to-stop-javascript-foreach – Ashutosh Chamoli Dec 11 '18 at 14:23
  • 3
    BTW, wouldn't a linear search be just `n` repetitions? – Kenna Jun 15 '20 at 20:20
  • see this link: https://www.measurethat.net/Benchmarks/Show/4261/0/find-vs-forof-vs-for-loop – mohammad javad ahmadi Sep 15 '21 at 06:06
4

Tried this:

var startTime, endTime;

function start() {
  startTime = new Date();
};

function end() {
  endTime = new Date();
  var timeDiff = endTime - startTime; //in ms
  console.log(timeDiff + " milliseconds");
}

let employees = [];
for (var i = 10000; i > 0; i--){
  let thisEmployee = {
    id: i,
    name: "Person" + i
  }
  employees.push(thisEmployee);
}

let id = 1;
let employee1 = null;
start();
for (const e of employees) {
    if (e.id === id) {
        employee1 = e;
        break;
    }
}
end();
console.log("Method1: ", JSON.stringify(employee1));
start();
let employee2 = employees.find(e => e.id === id);
end();
console.log("Method2: ", JSON.stringify(employee2));

First method is much slower:

"12 milliseconds"
"Method1: "
"{\"id\":1,\"name\":\"Person1\"}"
"0 milliseconds"
"Method2: "
"{\"id\":1,\"name\":\"Person1\"}"
Matt Kuhns
  • 1,328
  • 1
  • 13
  • 26
  • 1
    When measuring performance it's better to use [`performance.now()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) or [`console.time(); console.timeEnd()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) which provide higher resolution. – yuriy636 Jun 13 '18 at 19:26
3

I implemented both approaches as two methods with the same signature (forBreakMethod(x) and findMethod (x)) and passed them through a simple performance test spec.

(() => {

const test = (DATA_LENGTH = 1000, INDEX = 9, TESTS_COUNT = 10000) => {
  // data initialization
  const employees = [];
  for (let i = 1; i <= DATA_LENGTH; i++){
    employees.push({ id: i });
  }
  
  // methods initialization
  const forBreakMethod = (x) => {
    const length = employees.length;
    for (let i = 0; i < length; i++) {
      if (x === employees.id) {
        return employees[i];
      }
    }
  }
  const findMethod = (x) => {
    return employees.find(item => x === item.id);
  }
  
  // for-break test
  const time1 = performance.now();
  for (let i = 0; i < TESTS_COUNT; i++) {
    forBreakMethod(INDEX);
  }
  const time2 = performance.now();
  console.log(`[for-break] find ${INDEX} from ${DATA_LENGTH}: ${time2 - time1}`);
  
  // find test
  const time3 = performance.now();
  for (let i = 0; i < TESTS_COUNT; i++) {
    findMethod(INDEX);
  }
  const time4 = performance.now();
  console.log(`[Array.find] find ${INDEX} from ${DATA_LENGTH}: ${time4 - time3}`);

  console.log('---------------');
};

test(10, 1, 1000000);
test(10, 5, 1000000);
test(10, 9, 1000000);
console.log('\n');
test(100, 10, 100000);
test(100, 50, 100000);
test(100, 99, 100000);
console.log('\n');
test(1000, 10, 10000);
test(1000, 500, 10000);
test(1000, 999, 10000);
console.log('\n');
test(10000, 10, 10000);
test(10000, 5000, 10000);
test(10000, 9999, 10000);

})();

A conclusion I see is that the Array.find approach has an advantage if the item we want to find lives in the left part of the data array, but its performance getting low when the result index goes to the right. The for-break approach seems more stable, since its performance does not depend on the index we want to find, but its cost is significant.

So very rough, I would say that the Array.find approach could be considered as more performant if we are going to walk through the first half of the data array, otherwise I would use the for-break approach.

PS Chrome, Safari, Firefox, 2018.

dhilt
  • 18,707
  • 8
  • 70
  • 85
  • Excellent work, so does this mean with larger arrays for break is faster? – jjwallace Apr 23 '19 at 20:56
  • @jjwallace Well, generally I wouldn't say yes and wouldn't say no. But this tiny research shows that for large array with random index to find the `for-break` approach seems a bit faster than the `Array.find` one. Because for the right border we see more than 50% time increase for `Array.find`: 56 + 57 < 120 in the last test in my environment. – dhilt Apr 23 '19 at 21:51
1

It is fairly obvious that the native find() function is faster than a loop algorithm. But the OP asked "Is there any benefit to using a loop...?" A theoretical reason someone might want to loop is if they needed to process non-matching elements along the way.

MarkHu
  • 1,694
  • 16
  • 29
0

So I just tried this:

const array = [0, 1, 2, 3, 4, 5,];

for (const i of array) {
  console.log(i);
  if (i === 3)
    break;
}

array.find(i => {
  console.log(i);
  return i === 3;
});

Both of them output

0
1
2
3

So, they both short circuit on the first answer they find as I would expect, but as for specific performance I can't say for certain whether one is better than the other. I imagine the performance would be comparable if not identical.
The one big difference that stands out to me is that find returns the value, but the for loop you have to handle the value in the loop, otherwise assign it to a variable to use later. A minor detail but it could potentially make find much more readable for someone else looking at your code.

obermillerk
  • 1,560
  • 2
  • 11
  • 12