Does this for loop iterate multiple times?












11














I have been discussing some code with colleagues:



for(const a of arr) {
if(a.thing)
continue;
// do a thing
}


A suggestion was to filter this and use a forEach



arr.filter(a => a.thing)
.forEach(a => /* do a thing */);


There was a discussion about iterating more than necessary. I've looked this up, and I can't find anything. I also tried to figure out how to view the optimized output, but I don't know how to do that either.



I would expect that the filter and forEach turn into code that is very much like the for of with the continue, but I don't know how to be sure.



How can I find out? The only thing I've tried so far is google.










share|improve this question




















  • 1




    The second snippet definitely loops twice, while the first one would loop only once.
    – Taplar
    7 hours ago






  • 2




    btw, the first approach is faster, even if you take a single forEach.
    – Nina Scholz
    7 hours ago






  • 1




    Good question! Just as a side note I would use for of instead of for in to iterate over an array.
    – kev
    7 hours ago






  • 2




    By the way, these two snippets are not functionally identical. filter predicates are written in the positive sense, in other words, filter will return entries which match, rather than discarding matches. So you should remove the !.
    – Brandon
    6 hours ago






  • 1




    @Brandon Actually, the ! is required in this case, since the action is supposed to happen if a.thing is falsy (the code hits continue when a.thing is truthy in the first example). The code was correct before.
    – Brian McCutchon
    4 hours ago


















11














I have been discussing some code with colleagues:



for(const a of arr) {
if(a.thing)
continue;
// do a thing
}


A suggestion was to filter this and use a forEach



arr.filter(a => a.thing)
.forEach(a => /* do a thing */);


There was a discussion about iterating more than necessary. I've looked this up, and I can't find anything. I also tried to figure out how to view the optimized output, but I don't know how to do that either.



I would expect that the filter and forEach turn into code that is very much like the for of with the continue, but I don't know how to be sure.



How can I find out? The only thing I've tried so far is google.










share|improve this question




















  • 1




    The second snippet definitely loops twice, while the first one would loop only once.
    – Taplar
    7 hours ago






  • 2




    btw, the first approach is faster, even if you take a single forEach.
    – Nina Scholz
    7 hours ago






  • 1




    Good question! Just as a side note I would use for of instead of for in to iterate over an array.
    – kev
    7 hours ago






  • 2




    By the way, these two snippets are not functionally identical. filter predicates are written in the positive sense, in other words, filter will return entries which match, rather than discarding matches. So you should remove the !.
    – Brandon
    6 hours ago






  • 1




    @Brandon Actually, the ! is required in this case, since the action is supposed to happen if a.thing is falsy (the code hits continue when a.thing is truthy in the first example). The code was correct before.
    – Brian McCutchon
    4 hours ago
















11












11








11


2





I have been discussing some code with colleagues:



for(const a of arr) {
if(a.thing)
continue;
// do a thing
}


A suggestion was to filter this and use a forEach



arr.filter(a => a.thing)
.forEach(a => /* do a thing */);


There was a discussion about iterating more than necessary. I've looked this up, and I can't find anything. I also tried to figure out how to view the optimized output, but I don't know how to do that either.



I would expect that the filter and forEach turn into code that is very much like the for of with the continue, but I don't know how to be sure.



How can I find out? The only thing I've tried so far is google.










share|improve this question















I have been discussing some code with colleagues:



for(const a of arr) {
if(a.thing)
continue;
// do a thing
}


A suggestion was to filter this and use a forEach



arr.filter(a => a.thing)
.forEach(a => /* do a thing */);


There was a discussion about iterating more than necessary. I've looked this up, and I can't find anything. I also tried to figure out how to view the optimized output, but I don't know how to do that either.



I would expect that the filter and forEach turn into code that is very much like the for of with the continue, but I don't know how to be sure.



How can I find out? The only thing I've tried so far is google.







javascript performance






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 5 hours ago

























asked 7 hours ago









loctrice

8971923




8971923








  • 1




    The second snippet definitely loops twice, while the first one would loop only once.
    – Taplar
    7 hours ago






  • 2




    btw, the first approach is faster, even if you take a single forEach.
    – Nina Scholz
    7 hours ago






  • 1




    Good question! Just as a side note I would use for of instead of for in to iterate over an array.
    – kev
    7 hours ago






  • 2




    By the way, these two snippets are not functionally identical. filter predicates are written in the positive sense, in other words, filter will return entries which match, rather than discarding matches. So you should remove the !.
    – Brandon
    6 hours ago






  • 1




    @Brandon Actually, the ! is required in this case, since the action is supposed to happen if a.thing is falsy (the code hits continue when a.thing is truthy in the first example). The code was correct before.
    – Brian McCutchon
    4 hours ago
















  • 1




    The second snippet definitely loops twice, while the first one would loop only once.
    – Taplar
    7 hours ago






  • 2




    btw, the first approach is faster, even if you take a single forEach.
    – Nina Scholz
    7 hours ago






  • 1




    Good question! Just as a side note I would use for of instead of for in to iterate over an array.
    – kev
    7 hours ago






  • 2




    By the way, these two snippets are not functionally identical. filter predicates are written in the positive sense, in other words, filter will return entries which match, rather than discarding matches. So you should remove the !.
    – Brandon
    6 hours ago






  • 1




    @Brandon Actually, the ! is required in this case, since the action is supposed to happen if a.thing is falsy (the code hits continue when a.thing is truthy in the first example). The code was correct before.
    – Brian McCutchon
    4 hours ago










1




1




The second snippet definitely loops twice, while the first one would loop only once.
– Taplar
7 hours ago




The second snippet definitely loops twice, while the first one would loop only once.
– Taplar
7 hours ago




2




2




btw, the first approach is faster, even if you take a single forEach.
– Nina Scholz
7 hours ago




btw, the first approach is faster, even if you take a single forEach.
– Nina Scholz
7 hours ago




1




1




Good question! Just as a side note I would use for of instead of for in to iterate over an array.
– kev
7 hours ago




Good question! Just as a side note I would use for of instead of for in to iterate over an array.
– kev
7 hours ago




2




2




By the way, these two snippets are not functionally identical. filter predicates are written in the positive sense, in other words, filter will return entries which match, rather than discarding matches. So you should remove the !.
– Brandon
6 hours ago




By the way, these two snippets are not functionally identical. filter predicates are written in the positive sense, in other words, filter will return entries which match, rather than discarding matches. So you should remove the !.
– Brandon
6 hours ago




1




1




@Brandon Actually, the ! is required in this case, since the action is supposed to happen if a.thing is falsy (the code hits continue when a.thing is truthy in the first example). The code was correct before.
– Brian McCutchon
4 hours ago






@Brandon Actually, the ! is required in this case, since the action is supposed to happen if a.thing is falsy (the code hits continue when a.thing is truthy in the first example). The code was correct before.
– Brian McCutchon
4 hours ago














3 Answers
3






active

oldest

votes


















5














Your first example (the for in loop) is O(n), which will execute n times (n being the size of the array).



Your second example (the filter forEach) is O(n+m), which will execute n times in the filter (n being the size of the array), and then m times (m being the size of the resulting array after the filter takes place).



As such, the first example is faster. However, in this type of example without an exceedingly large sample set the difference is probably measured in microseconds or nanoseconds.



With regards to compilation optimization, that is essentially all memory access optimization. The major interpreters and engines will all analyze issues in code relating to function, variable, and property access such as how often and what the shape of the access graph looks like; and then, with all of that information, optimize their hidden structure to be more efficient for access. Essentially no optimization so far as loop replacement or process analysis is done on the code as it for the most part is optimized while it is running (if a specific part of code does start taking an excessively long time, it may have its code optimized).




When first executing the JavaScript code, V8 leverages full-codegen which directly translates the parsed JavaScript into machine code without any transformation. This allows it to start executing machine code very fast. Note that V8 does not use intermediate bytecode representation this way removing the need for an interpreter.



When your code has run for some time, the profiler thread has gathered enough data to tell which method should be optimized.



Next, Crankshaft optimizations begin in another thread. It translates the JavaScript abstract syntax tree to a high-level static single-assignment (SSA) representation called Hydrogen and tries to optimize that Hydrogen graph. Most optimizations are done at this level.

-https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e




*While continue may cause the execution to go to the next iteration, it still counts as an iteration of the loop.






share|improve this answer



















  • 1




    Is there no lazy filtering stream construct in JS?
    – Alexander
    5 hours ago










  • @Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
    – interphx
    4 hours ago



















1














An easy way to see how many times each part of that statement is called would be to add log statements like so and run it in the Chrome console



var arr = [1,2,3,4];
arr.filter(a => {console.log("hit1") ;return a%2 != 0;})
.forEach(a => {console.log("hit2")});


"Hit1" should print to the console 4 times regardless in this case. If it were to iterate too many times, we'd see "hit2" output 4 times, but after running this code it only outputs twice. So your assumption is partially correct, that the second time it iterates, it doesn't iterate over the whole set. However it does iterate over the whole set once in the .filter and then iterates again over the part of the set that matches the condition again in the .filter



Another good place to look is in the MDN developer docs here especially in the "Polyfill" section which outlines the exact equivalent algorithm and you can see that .filter() here returns the variable res, which is what .forEach would be performed upon.



So while it overall iterates over the set twice, in the .forEach section it only iterates over the part of the set that matches the .filter condition.






share|improve this answer

















  • 1




    of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
    – Nina Scholz
    7 hours ago












  • Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
    – nkorai
    7 hours ago






  • 1




    I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
    – loctrice
    6 hours ago



















1














The right answer is "it really doesn't matter". Some previously posted answer states that the second approach is O(n+m), but I beg to differ. The same exact "m" operations will also run in the first approach. In the worst case, even if you consider the second batch of operations as "m" (which doesn't really make much sense - we're talking about the same n elements given as input - that's not how complexity analysis work), in the worst case m==n and the complexity will be O(2n), which is just O(n) in the end anyway.



To directly answer your question, yes, the second approach will iterate over the collection twice while the first one will do it only once. But that probably won't make any difference to you. In cases like these, you probably want to improve readability over efficiency. How many items does your collection have? 10? 100? It's better to write code that will be easier to maintain over time than to strive for maximum efficiency all the time - because most of the time it just doesn't make any difference.



Moreover, iterating more than once doesn't mean your code runs slower. It's all about what's inside each loop. For instance:



for (const item of arr) {
// do A
// do B
}


Is virtually the same as:



for (const item of arr) {
// do A
}
for (const item of arr) {
// do B
}


The for loop itself doesn't add any significant overhead to the CPU. Although you would probably want to write a single loop anyway, if your code readability is improved when you do two loops, go ahead and do it.





If you really need to be efficient, you don't want to iterate through the whole collection, not even once. You want some smarter way to do it: either divide and conquer (O(log n)) or use hash maps (O(1)). A hash map a day keeps the inefficiency away :-)





Now, back to your example, if I find myself iterating over and over and doing the same operation every time, I'd just run the filtering operation only once, at the beginning:



const things = ;
const notThings = ;

for (const item of arr) {
item.thing ? things.push(item) : notThings.push(item);
}


And then you can freely iterate over notThings, knowing that unwanted items were already filtered out. Makes sense?






share|improve this answer























  • It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
    – loctrice
    5 hours ago










  • You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
    – Lucio Paiva
    5 hours ago








  • 1




    If we follow your assumption, then I would agree with you.
    – loctrice
    5 hours ago










  • On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
    – Lucio Paiva
    5 hours ago












  • Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
    – Lucio Paiva
    5 hours ago













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53990895%2fdoes-this-for-loop-iterate-multiple-times%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes









5














Your first example (the for in loop) is O(n), which will execute n times (n being the size of the array).



Your second example (the filter forEach) is O(n+m), which will execute n times in the filter (n being the size of the array), and then m times (m being the size of the resulting array after the filter takes place).



As such, the first example is faster. However, in this type of example without an exceedingly large sample set the difference is probably measured in microseconds or nanoseconds.



With regards to compilation optimization, that is essentially all memory access optimization. The major interpreters and engines will all analyze issues in code relating to function, variable, and property access such as how often and what the shape of the access graph looks like; and then, with all of that information, optimize their hidden structure to be more efficient for access. Essentially no optimization so far as loop replacement or process analysis is done on the code as it for the most part is optimized while it is running (if a specific part of code does start taking an excessively long time, it may have its code optimized).




When first executing the JavaScript code, V8 leverages full-codegen which directly translates the parsed JavaScript into machine code without any transformation. This allows it to start executing machine code very fast. Note that V8 does not use intermediate bytecode representation this way removing the need for an interpreter.



When your code has run for some time, the profiler thread has gathered enough data to tell which method should be optimized.



Next, Crankshaft optimizations begin in another thread. It translates the JavaScript abstract syntax tree to a high-level static single-assignment (SSA) representation called Hydrogen and tries to optimize that Hydrogen graph. Most optimizations are done at this level.

-https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e




*While continue may cause the execution to go to the next iteration, it still counts as an iteration of the loop.






share|improve this answer



















  • 1




    Is there no lazy filtering stream construct in JS?
    – Alexander
    5 hours ago










  • @Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
    – interphx
    4 hours ago
















5














Your first example (the for in loop) is O(n), which will execute n times (n being the size of the array).



Your second example (the filter forEach) is O(n+m), which will execute n times in the filter (n being the size of the array), and then m times (m being the size of the resulting array after the filter takes place).



As such, the first example is faster. However, in this type of example without an exceedingly large sample set the difference is probably measured in microseconds or nanoseconds.



With regards to compilation optimization, that is essentially all memory access optimization. The major interpreters and engines will all analyze issues in code relating to function, variable, and property access such as how often and what the shape of the access graph looks like; and then, with all of that information, optimize their hidden structure to be more efficient for access. Essentially no optimization so far as loop replacement or process analysis is done on the code as it for the most part is optimized while it is running (if a specific part of code does start taking an excessively long time, it may have its code optimized).




When first executing the JavaScript code, V8 leverages full-codegen which directly translates the parsed JavaScript into machine code without any transformation. This allows it to start executing machine code very fast. Note that V8 does not use intermediate bytecode representation this way removing the need for an interpreter.



When your code has run for some time, the profiler thread has gathered enough data to tell which method should be optimized.



Next, Crankshaft optimizations begin in another thread. It translates the JavaScript abstract syntax tree to a high-level static single-assignment (SSA) representation called Hydrogen and tries to optimize that Hydrogen graph. Most optimizations are done at this level.

-https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e




*While continue may cause the execution to go to the next iteration, it still counts as an iteration of the loop.






share|improve this answer



















  • 1




    Is there no lazy filtering stream construct in JS?
    – Alexander
    5 hours ago










  • @Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
    – interphx
    4 hours ago














5












5








5






Your first example (the for in loop) is O(n), which will execute n times (n being the size of the array).



Your second example (the filter forEach) is O(n+m), which will execute n times in the filter (n being the size of the array), and then m times (m being the size of the resulting array after the filter takes place).



As such, the first example is faster. However, in this type of example without an exceedingly large sample set the difference is probably measured in microseconds or nanoseconds.



With regards to compilation optimization, that is essentially all memory access optimization. The major interpreters and engines will all analyze issues in code relating to function, variable, and property access such as how often and what the shape of the access graph looks like; and then, with all of that information, optimize their hidden structure to be more efficient for access. Essentially no optimization so far as loop replacement or process analysis is done on the code as it for the most part is optimized while it is running (if a specific part of code does start taking an excessively long time, it may have its code optimized).




When first executing the JavaScript code, V8 leverages full-codegen which directly translates the parsed JavaScript into machine code without any transformation. This allows it to start executing machine code very fast. Note that V8 does not use intermediate bytecode representation this way removing the need for an interpreter.



When your code has run for some time, the profiler thread has gathered enough data to tell which method should be optimized.



Next, Crankshaft optimizations begin in another thread. It translates the JavaScript abstract syntax tree to a high-level static single-assignment (SSA) representation called Hydrogen and tries to optimize that Hydrogen graph. Most optimizations are done at this level.

-https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e




*While continue may cause the execution to go to the next iteration, it still counts as an iteration of the loop.






share|improve this answer














Your first example (the for in loop) is O(n), which will execute n times (n being the size of the array).



Your second example (the filter forEach) is O(n+m), which will execute n times in the filter (n being the size of the array), and then m times (m being the size of the resulting array after the filter takes place).



As such, the first example is faster. However, in this type of example without an exceedingly large sample set the difference is probably measured in microseconds or nanoseconds.



With regards to compilation optimization, that is essentially all memory access optimization. The major interpreters and engines will all analyze issues in code relating to function, variable, and property access such as how often and what the shape of the access graph looks like; and then, with all of that information, optimize their hidden structure to be more efficient for access. Essentially no optimization so far as loop replacement or process analysis is done on the code as it for the most part is optimized while it is running (if a specific part of code does start taking an excessively long time, it may have its code optimized).




When first executing the JavaScript code, V8 leverages full-codegen which directly translates the parsed JavaScript into machine code without any transformation. This allows it to start executing machine code very fast. Note that V8 does not use intermediate bytecode representation this way removing the need for an interpreter.



When your code has run for some time, the profiler thread has gathered enough data to tell which method should be optimized.



Next, Crankshaft optimizations begin in another thread. It translates the JavaScript abstract syntax tree to a high-level static single-assignment (SSA) representation called Hydrogen and tries to optimize that Hydrogen graph. Most optimizations are done at this level.

-https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e




*While continue may cause the execution to go to the next iteration, it still counts as an iteration of the loop.







share|improve this answer














share|improve this answer



share|improve this answer








edited 7 hours ago

























answered 7 hours ago









Travis J

63.8k28148223




63.8k28148223








  • 1




    Is there no lazy filtering stream construct in JS?
    – Alexander
    5 hours ago










  • @Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
    – interphx
    4 hours ago














  • 1




    Is there no lazy filtering stream construct in JS?
    – Alexander
    5 hours ago










  • @Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
    – interphx
    4 hours ago








1




1




Is there no lazy filtering stream construct in JS?
– Alexander
5 hours ago




Is there no lazy filtering stream construct in JS?
– Alexander
5 hours ago












@Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
– interphx
4 hours ago




@Alexander No, at least nothing built-in, and the engines don't do stream fusion optimization (yet). Though there are libraries which provide lazy collections, such as Lazy.js.
– interphx
4 hours ago













1














An easy way to see how many times each part of that statement is called would be to add log statements like so and run it in the Chrome console



var arr = [1,2,3,4];
arr.filter(a => {console.log("hit1") ;return a%2 != 0;})
.forEach(a => {console.log("hit2")});


"Hit1" should print to the console 4 times regardless in this case. If it were to iterate too many times, we'd see "hit2" output 4 times, but after running this code it only outputs twice. So your assumption is partially correct, that the second time it iterates, it doesn't iterate over the whole set. However it does iterate over the whole set once in the .filter and then iterates again over the part of the set that matches the condition again in the .filter



Another good place to look is in the MDN developer docs here especially in the "Polyfill" section which outlines the exact equivalent algorithm and you can see that .filter() here returns the variable res, which is what .forEach would be performed upon.



So while it overall iterates over the set twice, in the .forEach section it only iterates over the part of the set that matches the .filter condition.






share|improve this answer

















  • 1




    of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
    – Nina Scholz
    7 hours ago












  • Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
    – nkorai
    7 hours ago






  • 1




    I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
    – loctrice
    6 hours ago
















1














An easy way to see how many times each part of that statement is called would be to add log statements like so and run it in the Chrome console



var arr = [1,2,3,4];
arr.filter(a => {console.log("hit1") ;return a%2 != 0;})
.forEach(a => {console.log("hit2")});


"Hit1" should print to the console 4 times regardless in this case. If it were to iterate too many times, we'd see "hit2" output 4 times, but after running this code it only outputs twice. So your assumption is partially correct, that the second time it iterates, it doesn't iterate over the whole set. However it does iterate over the whole set once in the .filter and then iterates again over the part of the set that matches the condition again in the .filter



Another good place to look is in the MDN developer docs here especially in the "Polyfill" section which outlines the exact equivalent algorithm and you can see that .filter() here returns the variable res, which is what .forEach would be performed upon.



So while it overall iterates over the set twice, in the .forEach section it only iterates over the part of the set that matches the .filter condition.






share|improve this answer

















  • 1




    of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
    – Nina Scholz
    7 hours ago












  • Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
    – nkorai
    7 hours ago






  • 1




    I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
    – loctrice
    6 hours ago














1












1








1






An easy way to see how many times each part of that statement is called would be to add log statements like so and run it in the Chrome console



var arr = [1,2,3,4];
arr.filter(a => {console.log("hit1") ;return a%2 != 0;})
.forEach(a => {console.log("hit2")});


"Hit1" should print to the console 4 times regardless in this case. If it were to iterate too many times, we'd see "hit2" output 4 times, but after running this code it only outputs twice. So your assumption is partially correct, that the second time it iterates, it doesn't iterate over the whole set. However it does iterate over the whole set once in the .filter and then iterates again over the part of the set that matches the condition again in the .filter



Another good place to look is in the MDN developer docs here especially in the "Polyfill" section which outlines the exact equivalent algorithm and you can see that .filter() here returns the variable res, which is what .forEach would be performed upon.



So while it overall iterates over the set twice, in the .forEach section it only iterates over the part of the set that matches the .filter condition.






share|improve this answer












An easy way to see how many times each part of that statement is called would be to add log statements like so and run it in the Chrome console



var arr = [1,2,3,4];
arr.filter(a => {console.log("hit1") ;return a%2 != 0;})
.forEach(a => {console.log("hit2")});


"Hit1" should print to the console 4 times regardless in this case. If it were to iterate too many times, we'd see "hit2" output 4 times, but after running this code it only outputs twice. So your assumption is partially correct, that the second time it iterates, it doesn't iterate over the whole set. However it does iterate over the whole set once in the .filter and then iterates again over the part of the set that matches the condition again in the .filter



Another good place to look is in the MDN developer docs here especially in the "Polyfill" section which outlines the exact equivalent algorithm and you can see that .filter() here returns the variable res, which is what .forEach would be performed upon.



So while it overall iterates over the set twice, in the .forEach section it only iterates over the part of the set that matches the .filter condition.







share|improve this answer












share|improve this answer



share|improve this answer










answered 7 hours ago









nkorai

115110




115110








  • 1




    of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
    – Nina Scholz
    7 hours ago












  • Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
    – nkorai
    7 hours ago






  • 1




    I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
    – loctrice
    6 hours ago














  • 1




    of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
    – Nina Scholz
    7 hours ago












  • Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
    – nkorai
    7 hours ago






  • 1




    I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
    – loctrice
    6 hours ago








1




1




of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
– Nina Scholz
7 hours ago






of course it iterates twice, if the first filtering returns some elements. but it's slower than the for loop. the only advantage of the second is to use callbacks with functional patterns.
– Nina Scholz
7 hours ago














Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
– nkorai
7 hours ago




Right, definitely slower. Just wanted to illustrate to them how they can break down the problem in the future if they want to analyze something like this real quick.
– nkorai
7 hours ago




1




1




I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
– loctrice
6 hours ago




I get your point, but I wasn't so much asking how the language itself worked. I think adding the console.log (and making it an actual function body) would make it harder to optimize, and create the same amount of operation even after optimization.
– loctrice
6 hours ago











1














The right answer is "it really doesn't matter". Some previously posted answer states that the second approach is O(n+m), but I beg to differ. The same exact "m" operations will also run in the first approach. In the worst case, even if you consider the second batch of operations as "m" (which doesn't really make much sense - we're talking about the same n elements given as input - that's not how complexity analysis work), in the worst case m==n and the complexity will be O(2n), which is just O(n) in the end anyway.



To directly answer your question, yes, the second approach will iterate over the collection twice while the first one will do it only once. But that probably won't make any difference to you. In cases like these, you probably want to improve readability over efficiency. How many items does your collection have? 10? 100? It's better to write code that will be easier to maintain over time than to strive for maximum efficiency all the time - because most of the time it just doesn't make any difference.



Moreover, iterating more than once doesn't mean your code runs slower. It's all about what's inside each loop. For instance:



for (const item of arr) {
// do A
// do B
}


Is virtually the same as:



for (const item of arr) {
// do A
}
for (const item of arr) {
// do B
}


The for loop itself doesn't add any significant overhead to the CPU. Although you would probably want to write a single loop anyway, if your code readability is improved when you do two loops, go ahead and do it.





If you really need to be efficient, you don't want to iterate through the whole collection, not even once. You want some smarter way to do it: either divide and conquer (O(log n)) or use hash maps (O(1)). A hash map a day keeps the inefficiency away :-)





Now, back to your example, if I find myself iterating over and over and doing the same operation every time, I'd just run the filtering operation only once, at the beginning:



const things = ;
const notThings = ;

for (const item of arr) {
item.thing ? things.push(item) : notThings.push(item);
}


And then you can freely iterate over notThings, knowing that unwanted items were already filtered out. Makes sense?






share|improve this answer























  • It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
    – loctrice
    5 hours ago










  • You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
    – Lucio Paiva
    5 hours ago








  • 1




    If we follow your assumption, then I would agree with you.
    – loctrice
    5 hours ago










  • On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
    – Lucio Paiva
    5 hours ago












  • Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
    – Lucio Paiva
    5 hours ago


















1














The right answer is "it really doesn't matter". Some previously posted answer states that the second approach is O(n+m), but I beg to differ. The same exact "m" operations will also run in the first approach. In the worst case, even if you consider the second batch of operations as "m" (which doesn't really make much sense - we're talking about the same n elements given as input - that's not how complexity analysis work), in the worst case m==n and the complexity will be O(2n), which is just O(n) in the end anyway.



To directly answer your question, yes, the second approach will iterate over the collection twice while the first one will do it only once. But that probably won't make any difference to you. In cases like these, you probably want to improve readability over efficiency. How many items does your collection have? 10? 100? It's better to write code that will be easier to maintain over time than to strive for maximum efficiency all the time - because most of the time it just doesn't make any difference.



Moreover, iterating more than once doesn't mean your code runs slower. It's all about what's inside each loop. For instance:



for (const item of arr) {
// do A
// do B
}


Is virtually the same as:



for (const item of arr) {
// do A
}
for (const item of arr) {
// do B
}


The for loop itself doesn't add any significant overhead to the CPU. Although you would probably want to write a single loop anyway, if your code readability is improved when you do two loops, go ahead and do it.





If you really need to be efficient, you don't want to iterate through the whole collection, not even once. You want some smarter way to do it: either divide and conquer (O(log n)) or use hash maps (O(1)). A hash map a day keeps the inefficiency away :-)





Now, back to your example, if I find myself iterating over and over and doing the same operation every time, I'd just run the filtering operation only once, at the beginning:



const things = ;
const notThings = ;

for (const item of arr) {
item.thing ? things.push(item) : notThings.push(item);
}


And then you can freely iterate over notThings, knowing that unwanted items were already filtered out. Makes sense?






share|improve this answer























  • It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
    – loctrice
    5 hours ago










  • You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
    – Lucio Paiva
    5 hours ago








  • 1




    If we follow your assumption, then I would agree with you.
    – loctrice
    5 hours ago










  • On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
    – Lucio Paiva
    5 hours ago












  • Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
    – Lucio Paiva
    5 hours ago
















1












1








1






The right answer is "it really doesn't matter". Some previously posted answer states that the second approach is O(n+m), but I beg to differ. The same exact "m" operations will also run in the first approach. In the worst case, even if you consider the second batch of operations as "m" (which doesn't really make much sense - we're talking about the same n elements given as input - that's not how complexity analysis work), in the worst case m==n and the complexity will be O(2n), which is just O(n) in the end anyway.



To directly answer your question, yes, the second approach will iterate over the collection twice while the first one will do it only once. But that probably won't make any difference to you. In cases like these, you probably want to improve readability over efficiency. How many items does your collection have? 10? 100? It's better to write code that will be easier to maintain over time than to strive for maximum efficiency all the time - because most of the time it just doesn't make any difference.



Moreover, iterating more than once doesn't mean your code runs slower. It's all about what's inside each loop. For instance:



for (const item of arr) {
// do A
// do B
}


Is virtually the same as:



for (const item of arr) {
// do A
}
for (const item of arr) {
// do B
}


The for loop itself doesn't add any significant overhead to the CPU. Although you would probably want to write a single loop anyway, if your code readability is improved when you do two loops, go ahead and do it.





If you really need to be efficient, you don't want to iterate through the whole collection, not even once. You want some smarter way to do it: either divide and conquer (O(log n)) or use hash maps (O(1)). A hash map a day keeps the inefficiency away :-)





Now, back to your example, if I find myself iterating over and over and doing the same operation every time, I'd just run the filtering operation only once, at the beginning:



const things = ;
const notThings = ;

for (const item of arr) {
item.thing ? things.push(item) : notThings.push(item);
}


And then you can freely iterate over notThings, knowing that unwanted items were already filtered out. Makes sense?






share|improve this answer














The right answer is "it really doesn't matter". Some previously posted answer states that the second approach is O(n+m), but I beg to differ. The same exact "m" operations will also run in the first approach. In the worst case, even if you consider the second batch of operations as "m" (which doesn't really make much sense - we're talking about the same n elements given as input - that's not how complexity analysis work), in the worst case m==n and the complexity will be O(2n), which is just O(n) in the end anyway.



To directly answer your question, yes, the second approach will iterate over the collection twice while the first one will do it only once. But that probably won't make any difference to you. In cases like these, you probably want to improve readability over efficiency. How many items does your collection have? 10? 100? It's better to write code that will be easier to maintain over time than to strive for maximum efficiency all the time - because most of the time it just doesn't make any difference.



Moreover, iterating more than once doesn't mean your code runs slower. It's all about what's inside each loop. For instance:



for (const item of arr) {
// do A
// do B
}


Is virtually the same as:



for (const item of arr) {
// do A
}
for (const item of arr) {
// do B
}


The for loop itself doesn't add any significant overhead to the CPU. Although you would probably want to write a single loop anyway, if your code readability is improved when you do two loops, go ahead and do it.





If you really need to be efficient, you don't want to iterate through the whole collection, not even once. You want some smarter way to do it: either divide and conquer (O(log n)) or use hash maps (O(1)). A hash map a day keeps the inefficiency away :-)





Now, back to your example, if I find myself iterating over and over and doing the same operation every time, I'd just run the filtering operation only once, at the beginning:



const things = ;
const notThings = ;

for (const item of arr) {
item.thing ? things.push(item) : notThings.push(item);
}


And then you can freely iterate over notThings, knowing that unwanted items were already filtered out. Makes sense?







share|improve this answer














share|improve this answer



share|improve this answer








edited 5 hours ago

























answered 6 hours ago









Lucio Paiva

6,23023553




6,23023553












  • It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
    – loctrice
    5 hours ago










  • You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
    – Lucio Paiva
    5 hours ago








  • 1




    If we follow your assumption, then I would agree with you.
    – loctrice
    5 hours ago










  • On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
    – Lucio Paiva
    5 hours ago












  • Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
    – Lucio Paiva
    5 hours ago




















  • It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
    – loctrice
    5 hours ago










  • You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
    – Lucio Paiva
    5 hours ago








  • 1




    If we follow your assumption, then I would agree with you.
    – loctrice
    5 hours ago










  • On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
    – Lucio Paiva
    5 hours ago












  • Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
    – Lucio Paiva
    5 hours ago


















It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
– loctrice
5 hours ago




It's not multiple times for the same operation. Your example is just manually implementing a filter method. I don't see how that is different than just calling filter.
– loctrice
5 hours ago












You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
– Lucio Paiva
5 hours ago






You could just go ahead and call filter() (and I would probably do that myself too). My point is that if you're doing the check over and over for the same collection, you should probably filter it out from the start.
– Lucio Paiva
5 hours ago






1




1




If we follow your assumption, then I would agree with you.
– loctrice
5 hours ago




If we follow your assumption, then I would agree with you.
– loctrice
5 hours ago












On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
– Lucio Paiva
5 hours ago






On the other hand, if your code does that O(n) only once during the whole program execution, then go ahead and do what you're doing already - that's perfectly fine and it doesn't matter if you use filter()+forEach() or for of.
– Lucio Paiva
5 hours ago














Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
– Lucio Paiva
5 hours ago






Not sure if I made myself clear enough, so I edited my answer and added the part "Moreover, iterating more than once...". Please check it out and see if it makes sense to you.
– Lucio Paiva
5 hours ago




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53990895%2fdoes-this-for-loop-iterate-multiple-times%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Eastern Orthodox Church

Zagreb

Understanding the information contained in the Deep Space Network XML data?