views:

1989

answers:

22

I want to do something different with the last loop iteration when performing 'foreach' on an object. I'm using Ruby but the same goes for C#, Java etc.

  list = ['A','B','C']
  list.each{|i|
    puts "Looping: "+i # if not last loop iteration
    puts "Last one: "+i # if last loop iteration
  }

The output desired is equivalent to:

  Looping: 'A'
  Looping: 'B'
  Last one: 'C'

The obvious workaround is to migrate the code to a for loop using 'for i in 1..list.length', but the for each solution feels more graceful. What is the most graceful way to code a special case during a loop? Can it be done with foreach?

+2  A: 

Foreach is elegant in that it has no concern for the number of items in a list and treats each element equally, I think your only solution will be using a for loop that either stops at itemcount-1 and then you present your last item outside of the loop or a conditional within the loop that handles that specific condition, i.e. if (i==itemcount) { ... } else { ... }

Lazarus
Well I'm wondering if the 'itemcount' is available within the loop. Because it's a foreach loop, it has no count and only knows the current value, which is not know in advanced. So I would have nothing to compare against in a if statement.
MattChurchy
Oh never mind, you did say 'for' loop. Is a shame though, that I can't get the current position in my list, while in a foreach loop.
MattChurchy
If the position in a list is important to you then an indexed for loop *is* the elegant solution, just because it's common and often misused doesn't make it inelegant.
Lazarus
+2  A: 

At least in C# that's not possible without a regular for loop.

The enumerator of the collection decides whether a next elements exists (MoveNext method), the loop doesn't know about this.

DanielR
+18  A: 

Is this elegant enough? It assumes a non-empty list.

  list[0,list.length-1].each{|i|
    puts "Looping:"+i # if not last loop iteration
  }
  puts "Last one:" + list[list.length-1]
kgiannakakis
Somebody obviously objected, But I like this suggestion, it is thinking outside the box and would certainly do the job.
MattChurchy
+1 for creative solution :)
BengtBe
Nice. You could also use list[0...-1] and list[-1] instead of list[0, list.length-1] and list[list.length-1] respectively.
Firas Assaad
Note that the last in a Ruby Array is also list.last, which might make that final line a little more clear.
Mike Woodhouse
+2  A: 

You should use foreach only if you handle each one same. Use index based interation instead. Else you must add a different structure around the items, which you can use to differentiate the normal from last one in the foreach call (look at good Papers about the map reduced from google for the background: http://labs.google.com/papers/mapreduce.html, map == foreach, reduced == e.g. sum or filter).

Map has no knowledge about the structure (esp. which position a item is), it only transforms one item by item (no knowledge from one item can be used to transform an other!), but reduce can use a memory to for example count the position and handle the last item.

A common trick is to reverse the list and handle the first (which has now a known index = 0), and later apply reverse again. (Which is elegant but not fast ;) )

KlausMeier
In many cases you can't use index based iteration, because not all types of collections support it. For instance, take the IEnumerable interface (.NET) : you can enumerate the items, but not access them by index... you can't even know how many items there are in the enumeration. So foreach is the only way to go in that case...
Thomas Levesque
+26  A: 

The foreach construct (in Java definitely, probably also in other languages) is intended to represent the most general kind if iteration, which includes iteration over collections that have no meaningful iteration order. For example, a hash-based set does not have an ordering, and therefore there is no "last element". The last iteration may yield a different element each time you iterate.

Basically: no, the foreach construct is not meant to be used that way.

Michael Borgwardt
+1 baffles me that the .Length/.Count answers above have had so many votes given this
annakata
Doing something different on last element doesn't mean you want a specific element to be the last. For example, if you have a List of String that you needs to concatenate into one big String with separators. You will loop on each String, concatenate the String, then concatenate your separator. On the last String, you don't want the separator to be added. So even though i agree that doing something specific for the last element is breaking a bit the logic behind "foreach", the fact that the last element isn't always the same is completly irrelevant.
Ksempac
Although the foreach statement only requires the Iterator pattern and not a specific order, it doesn't mean that it isn't useful for ordered collections. In fact it is better from the classic for with index loop since it saves you from out of bounds errors.
kgiannakakis
@Ksempac - fair enough, I felt guilty about writing that anyway :)
annakata
The fact that a construct does not ALWAYS possess a certain property tells us nothing about whether or not it contains that property in the particular case of interest to us right now. If the original poster was intending to write a generic piece of code that would accept any iterable set and perform this operation, perhaps that would be a bad idea. But if he's talking about doing it in one particular program that he is writing, where he knows additional characteristics of the set, there's nothing wrong with using that knowledge.
Jay
A: 

How about obtaining a reference to the last item first and then use it for comparison inside the foreach loop? I am not say that you should do this as I myself would use the index based loop as mentioned by KlauseMeier. And sorry I don't know Ruby so the following sample is in C#! Hope u dont mind :-)

string lastItem = list[list.Count - 1];
foreach (string item in list) {
    if (item != lastItem)
     Console.WriteLine("Looping: " + item);
    else Console.Writeline("Lastone: " + item);
}

I revised the following code to compare by reference not value (can only use reference types not value types). the following code should support multiple objects containing same string (but not same string object) since MattChurcy's example did not specify that the strings must be distinct and I used LINQ Last method instead of calculating the index.

string lastItem = list.Last();
foreach (string item in list) {
    if (!object.ReferenceEquals(item, lastItem))
        Console.WriteLine("Looping: " + item);
    else        Console.WriteLine("Lastone: " + item);
}

Limitations of the above code. (1) It can only work for strings or reference types not value types. (2) Same object can only appear once in the list. You can have different objects containing the same content. Literal strings cannot be used repeatedly since C# does not create a unique object for strings that have the same content.

And i no stupid. I know an index based loop is the one to use. I already said so when i first posted the initial answer. I provided the best answer I can in the context of the question. I am too tired to keep explaining this so can you all just vote to delete my answer. I'll be so happy if this one goes away. thanks

anonymous
That's another out the box idea. I like this and wouldn't have thought of it myself. This could actually work quite well for my problem. It would also allow special cases elsewhere in the loop.
MattChurchy
C# 3.0 will also have the .Last operator on the array
Kirschstein
@Kirchstein: If the collection doesn't implement IList<T> then calling the Last method will involve evaluating the entire sequence so performance could be poor. If the collection does implement IList<T> then you can just do something like "var lastItem = list[list.Count - 1]" as TomatoGG does above.
LukeH
@TomatoGG: If the list items can be accessed directly by index then why not just use a standard "for" loop?
LukeH
this is a bug-prone code. You actually compare by value, not by index. If there is an object which equals to the last one, you'll have several "lastone: " lines.
modosansreves
@modosansreves: you are right
klew
@luke becoz MattChurchy didn't say he cannot access using a for loop, he just wants to use foreach because it was more "graceful". I was simply answering his question as best I can. Since I do not know how to access the last item in Ruby, I did my best to approx. with C#. Like I said I prefer to use an index based loop myself if I have to write such code.
anonymous
@modosanreves, I wasnt planning to compare by value. In my first paragraph, I mention "obtaining a reference". So I assume that ppl would understand that I meant to compare references, not values. I am new to stackoverflow so i though code was only for illustration. If I knew you guys gonna dissect my code line by line I guess I have to spend more time writing it in the future to be as clear as possible. So for anyone looking at the code, please replace item != lastItem to !item.ReferenceEquals(lastItem) ok? :-P – TomatoGG 0 secs ago
anonymous
It's not more graceful to use a `foreach` loop than a `for` loop when what you actually need is a `for` loop. And you need a `for` loop because you need the current index in order to determine whether you are currently on the last index.
Justice
wow this stackoverflow thing is really tough. don't really know how should i approach a question. should I try to provide a solution according to what the questioner wants or be an evangelist of standards and best practices by refusing to provide an answer that falls out of line?
anonymous
@TomatoGG: Sorry, your edit is inferior to your original version - (1) It won't display "Last one" at all if the list contains value types. (2) Even when the list contains ref types, it still suffers from the same problem as your original if the same object appears several times in the collection. Try it with {"A","B","C","A","B","C"} and see what happens.
LukeH
@TomatoGG: For this to work correctly then you need to find the last item by index, *not* by value and *not* by reference. That's why an elegant solution isn't really available using "foreach" if the collection isn't accessible by index, and if the collection is accessible by index then a plain "for" loop will provide a more elegant solution than "foreach".
LukeH
+2  A: 

You could do something like that (C#) :

string previous = null;
foreach(string item in list)
{
    if (previous != null)
        Console.WriteLine("Looping : {0}", previous);
    previous = item;
}
if (previous != null)
    Console.WriteLine("Last one : {0}", previous);
Thomas Levesque
What's wrong with that answer ? Voting down with no explanation really is useless...
Thomas Levesque
Again this is different from other answers, which is great. I like diverse answers. However I'm not certain this would print all the items in the list.
MattChurchy
It does work. I tested it!
anonymous
aha I see ur point!
anonymous
@MattChurchy, @TomatoGG: It will print all non-null items in the list, but it falls down if the list contains any nulls.
LukeH
I'm still not sure what this would do without compiling. But thank you for your answer. I think perhaps that the difficulty to interpret that the result would be rules it out as a elegant solution.
MattChurchy
Tested it, does work. I'm finally with you. The 'previous' variable isn't a flag but holds the result from the previous loop. Pushing the last variable in the list outside the loop. - Thanks again for you answer
MattChurchy
Seems like an elegant enough solution to me, simply deferring action until the next loop and using the storage mechanism to remember the last item. As Luke points out the "null" is unfortunate, but in general the idea is there - suggest initing to some constant GUID instead of null.
annakata
@Luke : you're right, I was assuming a list of non-null items... it would probably be better to have a 'firstPass' variable in addition to the 'previous' variable
Thomas Levesque
+8  A: 

You can define an eachwithlast method in your class to do the same as each on all elements but the last, but something else for the last:

class MyColl
  def eachwithlast
    for i in 0...(size-1)
      yield(self[i], false)
    end
    yield(self[size-1], true)
  end
end

Then you could call it like this (foo being an instance of MyColl or a subclass thereof):

foo.eachwithlast do |value, last|
  if last
    puts "Last one: "+value
  else
    puts "Looping: "+value
  end
end

Edit: Following molf's suggestion:

class MyColl
  def eachwithlast (defaultAction, lastAction)
    for i in 0...(size-1)
      defaultAction.call(self[i])
    end
    lastAction.call(self[size-1])
  end
end

foo.eachwithlast(
    lambda { |x| puts "looping "+x },
    lambda { |x| puts "last "+x } )
Svante
Definitely liking that one. But i'm not sure I want to extend the built in Array class for this simple task.
MattChurchy
I think that this reluctance is not warranted. I guess it comes from the notion that methods "belong" to a class, as opposed to generic functions. I think that if you want new behaviour for Arrays, you have to do what it takes.
Svante
This would be even better if the condition inside the block could be avoided. If you make eachwithlast take two lambdas, one for items up to n-1 and one for item n, it would be a little more friendly, I think.
molf
Thanks for your answer Svante and molf. Slightly more idiomatic ruby code might use each_with_index, and ruby usually uses snake_case for variables and method names. Like the shamelessly ripping off code here: http://stackoverflow.com/questions/2241684/magic-first-and-last-indicator-in-a-loop-in-ruby-rails/2241770#2241770
Andrew Grimm
+2  A: 

Ruby also has each_index method:

list = ['A','B','C']
list.each_index{|i|
  if i < list.size - 1
    puts "Looping:"+list[i]
  else
    puts "Last one:"+list[i]
}

EDIT:

Or using each (corrected TomatoGG and Kirschstein solution):

list = ['A', 'B', 'C', 'A']
list.each { |i|
   if (i.object_id != list.last.object_id)
      puts "Looping:#{i}"
   else
      puts "Last one:#{i}"
   end
}

Looping:A
Looping:B
Looping:C
Last one:A

Or

list = ['A', 'B', 'C', 'A']
list.each {|i| 
  i.object_id != list.last.object_id ? puts "Looping:#{i}" : puts "Last one:#{i}"       
}
klew
Good stuff, if id known it was there I'd probably of used it a few times by now. Cheers
MattChurchy
+1  A: 

I think I prefer kgiannakakis's solution, however you could always do something like this;

list = ['A','B','C']
list.each { |i|
   if (i != list.last)
      puts "Looping:#{i}"
   else
      puts "Last one:#{i}"
   end
}
Kirschstein
Thanks this seems to be a tighter syntax for TomatoGG's answer
MattChurchy
You should add "end" after last puts, and this solution didn't work if you have list = ['A', 'B', 'C', 'A'] - it will print "Last one: A" two times.
klew
@klew, thanks. Fixed.
Kirschstein
+1  A: 

If you're using a collection that exposes a Count property - an assumption made by many of the other answers, so I'll make it too - then you can do something like this using C# and LINQ:

foreach (var item in list.Select((x, i) => new { Val = x, Pos = i }))
{
    Console.Write(item.Pos == (list.Count - 1) ? "Last one: " : "Looping: ");
    Console.WriteLine(item.Val);
}

If we additionally assume that the items in the collection can be accessed directly by index - the currently accepted answer assumes this - then a plain for loop will be more elegant/readable than a foreach:

for (int i = 0; i < list.Count; i++)
{
    Console.Write(i == (list.Count - 1) ? "Last one: " : "Looping: ");
    Console.WriteLine(list[i]);
}

If the collection doesn't expose a Count property and can't be accessed by index then there isn't really any elegant way to do this, at least not in C#. A bug-fixed variation of Thomas Levesque's answer is probably as close as you'll get.


Here's the bug-fixed version of Thomas's answer:

string previous = null;
bool isFirst = true;
foreach (var item in list)
{
    if (!isFirst)
    {
        Console.WriteLine("Looping: " + previous);
    }
    previous = item;
    isFirst = false;
}
if (!isFirst)
{
    Console.WriteLine("Last one: " + previous);
}

And here's how I would do it in C# if the collection doesn't expose a Count property and the items aren't directly accessible by index. (Notice that there's no foreach and the code isn't particularly succinct, but it will give decent performance over pretty much any enumerable collection.)

var e = list.GetEnumerator();
if (e.MoveNext())
{
    var item = e.Current;

    while (e.MoveNext())
    {
        Console.WriteLine("Looping: " + item);
        item = e.Current;
    }
    Console.WriteLine("Last one: " + item);
}
LukeH
A: 

<hello> what are we thinking here?

public static void main(String[] args) {
 // TODO Auto-generated method stub
 String str = readIndex();
 String comp[] = str.split("}");

 StringBuffer sb = new StringBuffer();
 for (String s : comp) {
  sb.append(s);
  sb.append("}\n");
 }
 System.out.println (sb.toString());
}

As a modeling notation, the influence of the OMT notation dominates (e. g., using rectangles for classes and objects). Though the Booch "cloud" notation was dropped, the Booch capability to specify lower-level design detail was embraced. The use case notation from Objectory and the component notation from Booch were integrated with the rest of the notation, but the semantic integration was relatively weak in UML 1.1, and was not really fixed until the UML 2.0 major revision.

Animesh
you must have typed this into a wrong window
modosansreves
yup he must have! LOL
anonymous
lol look at Animesh's other answer. completely off topic. maybe a bot?
dotjoe
A: 

Similar to kgiannakakis's answer:

list.first(list.size - 1).each { |i| puts "Looping: " + i }
puts "Last one: " + list.last
Firas Assaad
+9  A: 

In Ruby I'd use each_with_index in this situation

list = ['A','B','C']
last = list.length-1
list.each_with_index{|i,index|
  if index == last 
    puts "Last one: "+i 
  else 
    puts "Looping: "+i # if not last loop iteration
  end
}
David Burrows
Thanks I wasn't aware of this method and with definitely use in future.
MattChurchy
+2  A: 

C# 3.0 or newer

Firstly, I would write an extension method:

public static void ForEachEx<T>(this IEnumerable<T> s, Action<T, bool> act)
{
    IEnumerator<T> curr = s.GetEnumerator();

    if (curr.MoveNext())
    {
        bool last;

        while (true)
        {
            T item = curr.Current;
            last = !curr.MoveNext();

            act(item, last);

            if (last)
                break;
        }
    }
}

Then using the new foreach is very simple:

int[] lData = new int[] { 1, 2, 3, 5, -1};

void Run()
{
    lData.ForEachEx((el, last) =>
    {
        if (last)
            Console.Write("last one: ");
        Console.WriteLine(el);
    });
}
modosansreves
Very elegant, I like it ! However I would prefer 'while(!last)' rather than the explicit break... And 'last' needs to be initialized
Thomas Levesque
This is the first elegant solution I've seen that works on `Iterable` s that are not `List` s. +1.
finnw
+2  A: 

What you are trying to do seems just a little too advanced for the foreach-loop. However, you can use Iterators explicitly. For example, in Java, I would write this:

Collection<String> ss = Arrays.asList("A","B","C");
Iterator<String> it = ss.iterator();
while (it.hasNext()) {
    String s = it.next();
    if(it.hasNext())
        System.out.println("Looping: " + s);
    else 
        System.out.println("Last one: " + s);
}
Bruno De Fraine
+1  A: 

How about this one? just learnt a little Ruby. hehehe

list.collect {|x|(x!=list.last ? "Looping:"+x:"Lastone:"+x) }.each{|i|puts i}
anonymous
Good idea, but you're comparing by value, so a list like ['A', 'B', 'C', 'D', 'C'] will get two last ones. Perhaps if you used !x.equal?(list.last)
Firas Assaad
Ya i know I need to compare references rather than values but I don't know how to do that in Ruby yet. So equal? is for comparing references?
anonymous
I like the conciseness, I do happen to have a real world use where I know the array values are unique. (This is actually for test code rather then production use.) So for me this does the great. Though it doesn't strictly need collecting and printing separately.
MattChurchy
A: 

Eric Lippert had a question like this (sorry, forgot the link) at one point. One technique is to shift the problem so that you do not need to know what the last loop iteration is. An example (which was the problem he presented, IIRC) is trying to print a comma-separated list of items. Rather than use the rule, output(item); if (!last) {output(",");} , one can instead use the rule, if (!first) {output(",");} output(item); Now you need a different rule for the first item instead of a different rule for the last item. And checking if the current node is easy (set first to true before the loop starts, and false after the first iteration).

Brian
http://blogs.msdn.com/ericlippert/archive/2009/04/13/restating-the-problem.aspx
Eric Lippert
Is there a way to apply this kind of transformation to a problem where the last thing is some unrelated action -- not a symmetrical operation? Like maybe... for each person leaving the room, go through the door. The last person should also turn off the light.
slothbear
A: 

I notice a number of suggestions assume that you can find the last item in the list before beginning the loop, and then compare every item to this item. If you can do this efficiently, then the underlying data structure is likely a simple array. If that's the case, why bother with the foreach at all? Just write:

for (int x=0;x<list.size()-1;++x)
{
  System.out.println("Looping: "+list.get(x));
}
System.out.println("Last one: "+list.get(list.size()-1));

If you cannot retrieve an item from an arbitrary position efficiently -- like it the underlying structure is a linked list -- then getting the last item probably involved a sequential search of the entire list. Depending on the size of the list, that may be a performance issue. If this is a frequently-executed function, you might want to consider using an array or ArrayList or comparable structure so you can do it this way.

Sounds to me like you're asking, "What's the best way to put a screw in using a hammer?", when of course the better question to ask is, "What's the correct tool to use to put in a screw?"

Jay
+1  A: 

Would it be a viable solution for your case to just take the first/last elements out of your array before doing the "general" each run?

Like this:

list = ['A','B','C','D']
first = list.shift
last = list.pop

puts "First one: #{first}"
list.each{|i|
  puts "Looping: "+i
}
puts "Last one: #{last}"
Carlos Lima
A: 

This problem can be solved in an elegant way using pattern matching in a functional programming language such as F#:

let rec printList (ls:string list) = 
    match ls with
        | [last] -> "Last " + last
        | head::rest -> "Looping " + head + "\n" + printList (rest)
        | [] -> ""
eulerfx
+1  A: 

I don't know how for-each loops works in other languages but java. In java for-each uses the Iterable interface that is used by the for-each to get an Iterator and loop with it. Iterator has a method hasNext that you could use if you could see the iterator within the loop. You can actually do the trick by enclosing an already obtained Iterator in an Iterable object so the for loop got what it needs and you can get a hasNext method inside the loop.

List<X> list = ...

final Iterator<X> it = list.iterator();
Iterable<X> itw = new Iterable<X>(){
  public Iterator<X> iterator () {
    return it;
  }
}

for (X x: itw) {
  doSomething(x);
  if (!it.hasNext()) {
    doSomethingElse(x);
  }
}

You can create a class that wraps all this iterable and iterator stuff so the code looks like this:

IterableIterator<X> itt = new IterableIterator<X>(list);
for (X x: itit) {
  doSomething(x);
  if (!itit.hasNext()) {
    doSomethingElse(x);
  }
}