8

Possible Duplicate:
Foreach loop, determine which is the last iteration of the loop

foreach (DataRowView row in orderedTable.DefaultView)
{
    if(lasttime) do-something;
}

orderedtable is a datatable

does anyone know how to find out whether we are on the last foreach iteration? please keep in mind that i do have duplicates in orderedtable

Community
  • 1
  • 1
JOE SKEET
  • 7,616
  • 13
  • 44
  • 63
  • What is `lasttime`? What do you mean by that? – Oded Jan 17 '11 at 19:43
  • There is a good explanation of why this cannot be done here: http://stackoverflow.com/questions/43021/c-get-index-of-current-foreach-iteration – Sean Hill Jan 17 '11 at 19:46
  • 2
    At first I was like "Jon Skeet doesn't know this"!? And then I'm like "Oh, it's JOE". – Greg Jan 17 '11 at 19:47
  • A pure enumeration (implementing IEnumerator) by definition does not know if the current element is the last. It knows only about the current element, and when the next is required, then it knows if it's the last. You can have a look at the definition of IEnumerator here: http://msdn.microsoft.com/fr-fr/library/system.collections.ienumerator(v=vs.80).aspx. Now, if it's a collection or a List, then you can have the Count. – Simon Mourier Jan 17 '11 at 19:47
  • 1
    Is your name really Joe Skeet? Just wondering ^ ^ – BrokenGlass Jan 17 '11 at 19:49
  • @broke yes we have the same great-grandmother i found out – JOE SKEET Jan 17 '11 at 19:53
  • 4
    @broke he inherited the brains, and i inherited the good looks – JOE SKEET Jan 17 '11 at 19:53
  • @JOESKEET: I can't tell if you're joking or serious, but either way that was pretty damn funny. – user541686 Aug 17 '12 at 12:27

9 Answers9

17

The correct method that works in all cases is to use the IEnumerator<T> directly:

using (var enumerator = orderedTable.DefaultView.GetEnumerator())
{
    if (enumerator.MoveNext())
    {
        bool isLast;
        do
        {
            var current = enumerator.Current;
            isLast = !enumerator.MoveNext();
            //Do stuff here
        } while (!isLast);
    }
}

This method works even if your collection doesn't have a Count property, and even if it does, this method will be more efficient if the Count property is slow.

user541686
  • 189,354
  • 112
  • 476
  • 821
8

The foreach construct does not know such a thing, since it applies equally to unbounded lists. It just has no way of knowing what is a last item.

You can iterate the manual way as well, though:

for (int i = 0; i < orderedTable.DefaultView.Count; i++) {
    DataRowView row = orderedTable.DefaultView[i];
    if (i == orderedTable.DefaulView.Count - 1) {
        // dosomething
    }
}
Joey
  • 316,376
  • 76
  • 642
  • 652
  • (Cross your fingers and hope that the `Count` property exists and isn't slow...) – user541686 Jan 17 '11 at 19:51
  • Well, MSDN lists it. I didn't look for whether it may be optional or not (in fact, the thing with optional methods is something I only know from Java so far). – Joey Jan 17 '11 at 21:53
3

An alternative approach which I don't think anyone posted. This works well if you don't know the count ahead of time.

DataRowView lastRow;
foreach (DataRowView row in orderedTable.DefaultView)
{
    // Do something...

    lastRow = row;
}

if (lastRow != null)
{
  // Do something with last row
}
Nelson Rothermel
  • 8,471
  • 8
  • 56
  • 78
  • This method potentially requires code duplication, which we all like to avoid... – user541686 Jan 17 '11 at 19:58
  • 1
    @Lambert: Every row, including the last one, runs the main code block, so no duplication there. I suppose this way wouldn't work if you *don't* want to `// Do something...` on the last row, since it only lets you run code on the last row *in addition* to what every other row runs. – Nelson Rothermel Jan 18 '11 at 16:14
2

You will have to use a regular for loop if you want to have different behavior on the last item.

for (int i = 0; i < orderedTable.DefaultView.Count; i++)
{
    //do stuff
    if (i == orderedTable.DefaultView.Count - 1)
    {
        //do additional special stuff
    }
}

It's worth noting that "the other Skeet" has an implementation for a "smart enumerable" which supports a Last property. See the article here: http://msmvps.com/blogs/jon_skeet/archive/2007/07/27/smart-enumerations.aspx

With this you could write something like this (I might get the details wrong, haven't tried it out myself):

foreach (SmartEnumerable<DataRowView> item in new SmartEnumerable<DataRowView>(orderedTable.DefaultView))
{
    DataRowView row = item.Value;
    if(item.IsLast)
    {
       ///do special stuff
    }
}
BrokenGlass
  • 149,257
  • 27
  • 271
  • 318
0

If I understand your question, does this help?

int lastrow = 0;
foreach (DataRow row in table.Rows) // Loop over the rows.
{
    lastrow++;
    // Do Something
    if (lastrow == (table.Rows.Count - 1))
    {
        // Do something else
    }
}
  • would be better as a for loop, because for already introduces a local variable, but it does work. – caesay Oct 21 '12 at 02:01
0

If you're concerned in keeping track of your iteration, why not use a for instead or a foreach? Then you can simply do the following:

for(int i = 0; i<orderedTable.DefaultView.Count-1;i++)
{
   if(i==orderedTable.DefaultView.Count-1)
   {
      // Last Row
   }
}
George Johnston
  • 29,901
  • 26
  • 117
  • 169
0

Instead of using foreach get the IEnumerator. If MoveNext returns null the previous was the last one.

for would work too of course.

Emond Erno
  • 48,121
  • 11
  • 77
  • 106
0

You could possibly do something like the following:

foreach (DataRowView row in orderedTable.DefaultView)
{
    if(row == orderedTable.DefaultView.Last()) do-something;
}

But its pretty inefficient.

GWLlosa
  • 22,656
  • 17
  • 75
  • 108
0

The code you have posted is identical to:

if (orderedTable.DefaultView.Rows.Count > 0)
    do-something;

If that does not do what you want, you will have to explain what lastTime does.

Dour High Arch
  • 20,700
  • 27
  • 72
  • 83