Camilo Martin
Camilo Martin

Reputation: 37908

How do I know if I'm iterating on the last item of the collection?

I want to do something different on the last KeyValuePair of the Dictionary I'm iterating on.

For Each item In collection
    If ItsTheLastItem
        DoX()
    Else
        DoY()
    End If
Next 

Is this possible?

Re-Edit: I'm not using a Dictionary's values, I'm actually using a List of KeyValuePairs. I've converted them and didn't notice later, dumb me.

Upvotes: 5

Views: 10871

Answers (9)

Bob Joziasse
Bob Joziasse

Reputation: 71

Why not use:

List<string> list = new List<string>();
// add items

foreach (string text in list)
{
    if (text.Equals(list[list.Count - 1]))
    {
        // last item
    }
    else
    {
        // not last item
    }
}

Upvotes: 7

Jim Lahman
Jim Lahman

Reputation: 2767

I created a solution to build an IP String from 4 octets based on @CamiloMartin answer above.

 String sIP;
 StringBuilder sb = new StringBuilder();
 List<String> lstOctets= new List<string>{ usrIP1.Text, usrIP2.Text, usrIP3.Text, usrIP4.Text };
 foreach (String Octet in lstOctets.GetRange(0,lstOctets.Count - 1))
 {
    sb.Append(string.Format("{0:d}.", Octet));
 }
 if (lstOctets.Count == 4)
    sb.Append(string.Format("{0:d}", lstOctets[lstOctets.Count - 1]));
 else
     sb.Append(string.Format("{0:d}.0", lstOctets[lstOctets.Count - 1]));
 sIP = sb.ToString();

Upvotes: 1

Camilo Martin
Camilo Martin

Reputation: 37908

I just had an idea that doesn't use a counter.

For Each item In collection.GetRange(0, collection.Count - 1)
    DoY(item)
Next
DoX(collection.Last)

Edit: sorry this is for a List, I had converted it before and intellisense gave me List methods instead of Dictionary methods.

Upvotes: 2

Femaref
Femaref

Reputation: 61497

Unless you want to implement the foreach yourself, use a counter (C# code):

int count = collection.Count;
int counter = 0;

foreach(var item in collection)
{
  counter++;
  if(counter == count)
    DoX();
  else
    DoY();
}

Please note that this will only work with non-streamed IEnumerable<T>, also, depending on the implementation, Count would cause the collection to be walked twice.

Upvotes: 8

jason
jason

Reputation: 241789

I would make your own extension method. This implementation here guarantees you only walk the collection once.

static class IEnumerableExtensions {
    public static void ForEachExceptTheLast<T>(
        this IEnumerable<T> source,
        Action<T> usualAction,
        Action<T> lastAction
    ) {
        var e = source.GetEnumerator();
        T penultimate;
        T last;
        if (e.MoveNext()) {
            last = e.Current;
            while(e.MoveNext()) {
                penultimate = last;
                last = e.Current;
                usualAction(penultimate);
            }
            lastAction(last);
        }
    }
}    

Usage:

Enumerable.Range(1, 5)
          .ForEachExceptTheLast(
              x => Console.WriteLine("Not last: " + x),
              x => Console.WriteLine("Last: " + x)
);

Output:

Not last: 1
Not last: 2
Not last: 3
Not last: 4
Last: 5

Upvotes: 8

Jahan Zinedine
Jahan Zinedine

Reputation: 14874

        foreach (var item in collection.Reverse().Skip(1))
        {

        }
        // do here the stuff related to list.LastOrDefaut()

Another approach is to maintain the iteration index and when hit the collection.Length-2 or collection.Count()-2 then stop the loop and do whatever you want with last element.

But as @BrokenGlass said beware of side effects that it may has on your application.

And generally you must use Linq with special care. For example every time you iterate on a linq to sql collection, you execute the query again, so it's much better to run it once eagerly by simply calling .ToList() and use that in memory collection as much as you want.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1503729

You can't do this naturally with an IEnumerable<T> as you won't know whether there are any more elements until you try to get to the next one.

However, in my MiscUtil library I have something called SmartEnumerable which reads one item in advance, in order to provide the following properties:

  • IsFirst
  • IsLast
  • Index
  • Value

See this page for usage instructions. For example:

For Each item In collection.ToSmartEnumerable()
    If item.IsFirst
        DoX()
    Else
        DoY()
    End If
Next 

You'd need to use item.Value to get at the value of the current item.

One point about iterating over dictionaries though - they're not actually in any order. So while you can iterate and there will be a first entry and a last entry, you shouldn't assume that they'll be the first entry added and the last entry added respectively. That may not be a problem for your use case, but you ought to be aware of it.

Upvotes: 2

J&#252;rgen Steinblock
J&#252;rgen Steinblock

Reputation: 31743

With an Dictionary you have multiple options

Dim dictionary as new Dictionary(of String, MyObject)
For Each item in dictionary
    // item is KeyValue pair
Next

For Each item in dictionary.Keys
    // item is String
Next

For Each item in dictionary.Keys
    // item is MyObject
Next

I would suggest you itterate over the ValueCollection and either use a counter or do a Equals comparison between the current and the last item (which will probably be slower but you save a counter variable ;) Don't forget to add a check of an empty list.

For Each item in dictionary.Values

    If dictionary.Count > 0 AndAlso _
        item.Equals(dictionary.Values(dictionary.Count - 1)) Then
        Console.WriteLine("Last Item")
    Else
        Console.WriteLine("Some other Item")
    End If

Next

Upvotes: 0

fejesjoco
fejesjoco

Reputation: 11903

Convert the collection to a list (like LINQ's ToList()) and iterate with a for(int i = 0)... loop.

Upvotes: 3

Related Questions