How does foreach work when looping through function results?
The function's only called once, to return an IEnumerator<T>
; after that, the MoveNext()
method and the Current
property are used to iterate through the results:
foreach (Foo f in GetFoos())
{
// Do stuff
}
is somewhat equivalent to:
using (IEnumerator<Foo> iterator = GetFoos().GetEnumerator())
{
while (iterator.MoveNext())
{
Foo f = iterator.Current;
// Do stuff
}
}
Note that the iterator is disposed at the end - this is particularly important for disposing resources from iterator blocks, e.g.:
public IEnumerable<string> GetLines(string file)
{
using (TextReader reader = File.OpenText(file))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
In the above code, you really want the file to be closed when you finish iterating, and the compiler implements IDisposable
cunningly to make that work.
Why would for loop and forEach work different?
Return statement is equivalent of break, for loop can be broken, foreach can't be.
How do you get the index of the current iteration of a foreach loop?
The foreach
is for iterating over collections that implement IEnumerable
. It does this by calling GetEnumerator
on the collection, which will return an Enumerator
.
This Enumerator has a method and a property:
MoveNext()
Current
Current
returns the object that Enumerator is currently on, MoveNext
updates Current
to the next object.
The concept of an index is foreign to the concept of enumeration, and cannot be done.
Because of that, most collections are able to be traversed using an indexer and the for loop construct.
I greatly prefer using a for loop in this situation compared to tracking the index with a local variable.
C# foreach - Is collection computed with each iteration?
Foreach uses IEnumerable
interface, it retrieves the Enumerator once, and uses it to traverse the collection.
So it's perfectly safe to use foreach on a complex function, it doesn't re-calculate the collection each time.
Using async/await with a forEach loop
Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles
function does immediately return after that.
Reading in sequence
If you want to read the files in sequence, you cannot use forEach
indeed. Just use a modern for … of
loop instead, in which await
will work as expected:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
Reading in parallel
If you want to read the files in parallel, you cannot use forEach
indeed. Each of the async
callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map
instead, and you can await the array of promises that you'll get with Promise.all
:
async function printFiles () {
const files = await getFilePaths();
await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}
Loop over an array in JavaScript
TL;DR
Your best bets are usually
- a
for-of
loop (ES2015+ only; spec | MDN) - simple andasync
-friendlyfor (const element of theArray) {
// ...use `element`...
} forEach
(ES5+ only; spec | MDN) (or its relativessome
and such) - notasync
-friendly (but see details)theArray.forEach(element => {
// ...use `element`...
});- a simple old-fashioned
for
loop -async
-friendlyfor (let index = 0; index < theArray.length; ++index) {
const element = theArray[index];
// ...use `element`...
} - (rarely)
for-in
with safeguards -async
-friendlyfor (const propertyName in theArray) {
if (/*...is an array element property (see below)...*/) {
const element = theArray[propertyName];
// ...use `element`...
}
}
- a
Some quick "don't"s:
- Don't use
for-in
unless you use it with safeguards or are at least aware of why it might bite you. - Don't use
map
if you're not using its return value.
(There's sadly someone out there teachingmap
[spec / MDN] as though it wereforEach
— but as I write on my blog, that's not what it's for. If you aren't using the array it creates, don't usemap
.) - Don't use
forEach
if the callback does asynchronous work and you want theforEach
to wait until that work is done (because it won't).
- Don't use
But there's lots more to explore, read on...
JavaScript has powerful semantics for looping through arrays and array-like objects. I've split the answer into two parts: Options for genuine arrays, and options for things that are just array-like, such as the arguments
object, other iterable objects (ES2015+), DOM collections, and so on.
Okay, let's look at our options:
For Actual Arrays
You have five options (two supported basically forever, another added by ECMAScript 5 ["ES5"], and two more added in ECMAScript 2015 ("ES2015", aka "ES6"):
- Use
for-of
(use an iterator implicitly) (ES2015+) - Use
forEach
and related (ES5+) - Use a simple
for
loop - Use
for-in
correctly - Use an iterator explicitly (ES2015+)
(You can see those old specs here: ES5, ES2015, but both have been superceded; the current editor's draft is always here.)
Details:
1. Use for-of
(use an iterator implicitly) (ES2015+)
ES2015 added iterators and iterables to JavaScript. Arrays are iterable (so are strings, Map
s, and Set
s, as well as DOM collections and lists, as you'll see later). Iterable objects provide iterators for their values. The new for-of
statement loops through the values returned by an iterator:
const a = ["a", "b", "c"];
for (const element of a) { // You can use `let` instead of `const` if you like
console.log(element);
}
// a
// b
// c
Foreach loop, determine which is the last iteration of the loop
If you just need to do something with the last element (as opposed to something different with the last element then using LINQ will help here:
Item last = Model.Results.Last();
// do something with last
If you need to do something different with the last element then you'd need something like:
Item last = Model.Results.Last();
foreach (Item result in Model.Results)
{
// do something with each item
if (result.Equals(last))
{
// do something different with the last item
}
else
{
// do something different with every item but the last
}
}
Though you'd probably need to write a custom comparer to ensure that you could tell that the item was the same as the item returned by Last()
.
This approach should be used with caution as Last
may well have to iterate through the collection. While this might not be a problem for small collections, if it gets large it could have performance implications. It will also fail if the list contains duplicate items. In this cases something like this may be more appropriate:
int totalCount = result.Count();
for (int count = 0; count < totalCount; count++)
{
Item result = Model.Results[count];
// do something with each item
if ((count + 1) == totalCount)
{
// do something different with the last item
}
else
{
// do something different with every item but the last
}
}
Powershell 7 - ForEach -Parallel in a Function does not return anything when the result is added to the array
The problem here is that threads running in parallel have to alter the same array. There are methods to make that happen, but in this case it is enough to just emit $myobject from the function. I removed the array and the code to add $myObject to the array.
When the function is called the array is created automatically, so the result is the same.
Function Get-ResponseFromParallelPings($activeHops) {
$activeHops | ForEach-Object -Parallel {
$count = 5
$LatencyNumber = 0
$SuccessNumber = 0
$Answer = Test-Connection -count $count -targetname $_.Name -delay 1
foreach ($a in $Answer) {
$LatencyNumber += $a.Latency / $count
if ($a.Status -eq "Success") {
$IncreaseBy = 100 / $count
$SuccessNumber += $IncreaseBy
}
}
$myObject = [PSCustomObject]@{
DestinationIP = $_.Name
AverageLatency = $LatencyNumber
Success = $SuccessNumber
}
$myObject
}
}
C# - Does function get called for each iteration of a foreach loop?
It will be called just once.
P.S. Calling it multiple times would make little sense. You would call it each time anew if you expected the result to be different each time. And how would you iterate over a continuously changing set?
Changing a value inside the array in a forEach() loop
Suggestion : move newArray
outside the clicked function as it is going to update on click.
Implementation : You can use Array.filter() method on newArray
to check if record as per the input id is available or not and then by using Array.find() you can do filtering on the mainArray
if there is no data in newArray
.
Live demo :
const newArray = [];
function clicked(inp){
const mainArray = [
{ id: 1, name: "Shoes",stock: 5, price: 10 },
{ id: 2, name: "Bag",stock: 10, price: 50 },
];
const findInNewArr = newArray.filter(item => inp === item.id);
if (findInNewArr.length) {
newArray[0].stock += 1;
} else {
const findInMainArray = mainArray.find(element => inp === element.id);
if (findInMainArray) {
findInMainArray.stock = 1;
newArray.push(findInMainArray);
}
}
console.log(newArray);
}
<button id="1" onclick="clicked(2)">Click me</button>
Related Topics
C# - Fill a Combo Box with a Datatable
Notify Binding for Static Properties in Static Classes
Singleton by Jon Skeet Clarification
Converting from Ienumerable to List
Check If Value Exists in Datatable
How to Return a Custom Http Status Code from a Wcf Rest Method
Convert System.Drawing.Icon to System.Media.Imagesource
Initialization Order of Static Fields in Static Class
How to Get the Member to Which My Custom Attribute Was Applied
Call a Method from Another Form
C#: Prepending to Beginning of a File
Visual Studio Build Fails: Unable to Copy Exe-File from Obj\Debug to Bin\Debug
How to Discover the "Path" of an Embedded Resource
Convert String to Hex-String in C#
How to Detect When a Windows Form Is Being Minimized
What Is the Simplest C# Function to Parse a JSON String into an Object