简体   繁体   中英

Is it faster to iterate / loop over an object or an array?

So I've been doing this ProductSearchPage using React and it has a bunch of filter values that I need to set to filter my product list and show results.

Up until now, I've been handling my product list as an array (even though I'm fetching it as an object, I'm converting it to an array) and I've been using lots of map , forEach and A LOT of filter loops over those arrays over and over again.

  • I'll get a productList , I'll filter based on category
  • I'll take the new filteredList and filter based on priceFilters
  • I'll take the new filteredList and filter based on ratingFilter
  • And so on for brandFilter , featuresFilters , etc.

I began to think that I might be creating a black hole of iterations and that might hurt my performance at some point. I'm doing client side searching and filtering. We're talking about 2k products maximum.

So I wondered if it would be faster to iterate and filter over an object instead of an array. I would be deleting properties and creating new objects along the way.

So I did this snippet to test:

And for my surprise the results were a lot in favor of the array loops.

Looping object with for...in: 0.31ms
Looping array with forEach: 0.08ms
Looping array with filter: 0.10ms
Looping array with map: 0.09ms

QUESTION

Is this enough evidence that looping through arrays is faster than looping through objects and I should stick to the forEach , map and filter methods?

NOTE: This is really simplified. In my real case, each product is an object with some properties (some of them are nested properties). So my options are to keep the list as an array of object (like I've been doing so far) or I could keep a big object allProducts with each product as a property of that object. Could this change the results?

 const myObject = {}; const myArray = [] for (let i=0; i<=2000; i++) { myObject['prop'+i] = i; } for (let k=0; k<=2000; k++) { myArray[k] = k; } const t0 = window.performance.now(); for (const key in myObject) { if (myObject[key] % 37 === 0) { //console.log(myObject[key] + ' is a multiple of 37'); } } const t1 = window.performance.now(); console.log('Looping object with for...in: ' + (t1 - t0).toFixed(2) + 'ms'); const t2 = window.performance.now(); myArray.forEach((item) => { if (item % 37 === 0) { //console.log(item + ' is a multiple of 37'); } }); const t3 = window.performance.now(); console.log('Looping array with forEach: ' + (t3 - t2).toFixed(2) + 'ms'); const t4 = window.performance.now(); const newArray = myArray.filter((item) => item % 37 === 0); const t5 = window.performance.now(); console.log('Looping array with filter: ' + (t5 - t4).toFixed(2) + 'ms'); const t6 = window.performance.now(); const newArray2 = myArray.map((item) => item*2); const t7 = window.performance.now(); console.log('Looping array with map: ' + (t7 - t6).toFixed(2) + 'ms');

I would be deleting properties and creating new objects along the way.

These operations will likely take orders of magnitude longer than the time it takes to just perform the loop.

Unless of course the way you loop also affects the way you create or delete objects/properties, but I assume we're considering a loop that otherwise does identical instructions.

In the vast majority of cases it's a tiny part of the performance budget (like 1 millionth), and the wrong place to start if you want to optimize a complex application. Just run some profiling tools, get an overview of where the application is spending time, and focus on the slowest parts.

Is this enough evidence that looping through arrays is faster than looping through objects and I should stick to the forEach, map and filter methods?

No, because it's a single simplified example. It doesn't tell anything about how big of a chunk of the performance budget it represents. It's probably also different depending on which JS runtime is used. All you can derive from it is that with 2000 iterations it takes at worst 0.31 ms.

I expanded the example a bit by adding a very small amount of extra work inside the loop. This can then be multiplied to see how fast it starts being more significant than just the loop. See the iteration function below. It internally runs identically for both cases.

If the complexity is set to 0 (run extra work 0 times), it performs just like the results posted in the question. Array is 2 to 4 times faster.

However just running this work once, the difference is almost gone (~0.7ms vs ~0.8ms for me). From 2 times and upwards sometimes array wins, sometimes object, but never by a big margin.

So the difference becomes insignificant once you do pretty much anything at all inside the loop.

 const myObject = {}; const myArray = [] const iterations = 2000; for (let i = 0; i < iterations; i++) { myObject['prop' + i] = i; myArray[i] = i; } let total = 0; function iteration(a, complexity) { const x = {}; for (let i = 0; i < complexity; i++) { // Do some simple instructions const rand = Math.random(); x[`${a}~${i}`] = rand; total += rand; } return x; } function loopObject(complexity) { const results = []; for (const key in myObject) { results.push(iteration(myObject[key], complexity)); } return results; } function loopArray(complexity) { const results = []; myArray.forEach((item) => { results.push(iteration(item, complexity)) }); return results; } const samples = 10; const decimals = 6; function test(complexity) { console.log(`COMPLEXITY ${complexity} (${samples} samples)`) let arrayTimes = []; let objectTimes = []; for (let i = 0; i < samples; i++) { const tA = performance.now(); const resultArray = loopArray(complexity); arrayTimes.push(performance.now() - tA); const tO = performance.now(); const resultObject = loopObject(complexity); objectTimes.push(performance.now() - tO); } const arraySum = arrayTimes.reduce((p, c) => p + c, 0); const objectSum = objectTimes.reduce((p, c) => p + c, 0); const arrayWins = arraySum < objectSum; console.log( `ARRAY ${arrayWins? ' (winner)': ''} avg: ${(arraySum / samples).toFixed(decimals)} min: ${Math.min(...arrayTimes).toFixed(decimals)} max: ${Math.max(...arrayTimes).toFixed(decimals)}`); console.log( `OBJECT ${?arrayWins: ' (winner)': ''} avg. ${(objectSum / samples):toFixed(decimals)} min. ${Math.min(...objectTimes):toFixed(decimals)} max. ${Math.max(...objectTimes);toFixed(decimals)}`), } const complexities = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50; 100]. complexities;forEach(test);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM