Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Less-verbose way of handling the first pass through a foreach?

Tags:

I often find myself doing the following index-counter messiness in a foreach loop to find out if I am on the first element or not. Is there a more elegant way to do this in C#, something along the lines of if(this.foreach.Pass == 1) etc.?

int index = 0; foreach (var websitePage in websitePages) {     if(index == 0)         classAttributePart = " class=\"first\"";     sb.AppendLine(String.Format("<li" + classAttributePart + ">" +          "<a href=\"{0}\">{1}</a></li>",          websitePage.GetFileName(), websitePage.Title));     index++; } 
like image 826
Edward Tanguay Avatar asked Feb 21 '10 23:02

Edward Tanguay


People also ask

Why is forEach forward only?

That is because foreach is meant to iterate over a container, making sure each item is visited exactly once, without changing the container, to avoid nasty side effects.

Does forEach wait for completion?

Because forEach does not wait for each promise to resolve, all the prizes are awarded in parallel, not serial (one by one). So the loop actually finishes iterating before any of the prizes have finished being awarded!

What is index in forEach?

Luckily, there are several ways to get an index variable with foreach : Declare an integer variable before the loop, and then increase that one inside the loop with each loop cycle. Create a tuple that returns both the element's value and its index. Or swap the foreach loop with the for loop.

How do I get current index in forEach?

A callback function is a simple function that defines the operation to be performed on a single element, and the forEach() method makes sure it will be performed on each element of an array. The forEach() method has a pretty straightforward syntax: forEach(callback(currentElement, index, arr), thisValue);


2 Answers

Another approach is to accept that the "ugly part" has to be implemented somewhere and provide an abstraction that hides the "ugly part" so that you don't have to repeat it in multiple places and can focus on the specific algorithm. This can be done using C# lambda expressions (or using C# 2.0 anonymous delegates if you're restricted to .NET 2.0):

void ForEachWithFirst<T>(IEnumerable<T> en,       Action<T> firstRun, Action<T> nextRun) {   bool first = true;   foreach(var e in en) {     if (first) { first = false; firstRun(e); } else nextRun(e);   } } 

Now you can use this reusable method to implement your algorithm like this:

ForEachWithFirst(websitePages,   (wp => sb.AppendLine(String.Format("<li class=\"first\">" +          "<a href=\"{0}\">{1}</a></li>", wp.GetFileName(), wp.Title)))   (wp => sb.AppendLine(String.Format("<li>" +           "<a href=\"{0}\">{1}</a></li>", wp.GetFileName(), wp.Title))) ); 

You could design the abstraction differently depending on the exact repeating pattern. The good thing is that - thanks to lambda expression - the structure of the abstraction is completely up to you.

like image 64
Tomas Petricek Avatar answered Oct 14 '22 15:10

Tomas Petricek


Slightly less verbose:

string classAttributePart = " class=\"first\""; foreach (var websitePage in websitePages) {     sb.AppendLine(String.Format("<li" + classAttributePart + "><a href=\"{0}\">{1}</a></li>", websitePage.GetFileName(), websitePage.Title));     classAttributePart = string.Empty; } 

If you are using .NET 3.5 you could use the overload of Select that gives you the index and test that. Then you also wouldn't need the StringBuilder. Here's the code for that:

string[] s = websitePages.Select((websitePage, i) =>         String.Format("<li{0}><a href=\"{1}\">{2}</a></li>\n",                       i == 0 ? " class=\"first\"" : "",                       websitePage.GetFileName(),                       websitePage.Title)).ToArray();  string result = string.Join("", s); 

It looks a bit more verbose, but that's mainly because I broke the very long line many shorter ones.

like image 24
Mark Byers Avatar answered Oct 14 '22 14:10

Mark Byers