在为每个使用时识别最后一个循环

Mat*_*att 48 c# ruby java foreach loops

我想在对象上执行'foreach'时,对最后一个循环迭代做一些不同的事情.我正在使用Ruby,但C#,Java等也是如此.

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

所需的输出相当于:

  Looping: 'A'
  Looping: 'B'
  Last one: 'C'
Run Code Online (Sandbox Code Playgroud)

显而易见的解决方法是使用代码将代码迁移到for循环'for i in 1..list.length',但for each解决方案感觉更优雅.在循环期间编写特殊情况的最优雅方法是什么?可以用foreach完成吗?

Big*_*jim 38

我在这里看到很多复杂的,难以理解的代码......为什么不保持简单:

var count = list.Length;

foreach(var item in list)
    if (--count > 0)
        Console.WriteLine("Looping: " + item);
    else
        Console.Writeline("Lastone: " + item);
Run Code Online (Sandbox Code Playgroud)

这只是一个额外的声明!

另一种常见情况是,您希望使用最后一项执行额外或更少操作,例如在项目之间放置分隔符:

var count = list.Length;

foreach(var item in list)
{
    Console.Write(item);
    if (--count > 0)
        Console.Write(",");
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解决方案.非常快速的比较,只有一个长度查找!我现在将使用它一段时间. (2认同)

Mic*_*rdt 37

foreach结构(在Java中肯定也可能在其他语言中)用于表示迭代时最常见的类型,其中包括对没有有意义的迭代顺序的集合进行迭代.例如,基于散列的集合没有排序,因此没有 "最后一个元素".每次迭代时,最后一次迭代可能会产生不同的元素.

基本上:不,foreach结构并不意味着以这种方式使用.

  • 在最后一个元素上做一些不同的事情并不意味着您希望特定元素成为最后一个元素.例如,如果您有一个String of List,您需要将其连接成一个带分隔符的大字符串.您将循环每个String,连接String,然后连接您的分隔符.在最后一个String上,您不希望添加分隔符.因此即使我同意为最后一个元素做一些特定的事情会破坏"foreach"背后的逻辑,但最后一个元素并不总是相同的事实完全无关紧要. (34认同)
  • +1让我感到困惑的是,上面的.Length/.Count答案得到了这么多的票数 (3认同)

ano*_*ous 23

如何先获取对最后一项引用,然后在foreach循环中使用它进行比较?我并不是说你应该这样做,因为我自己会使用KlauseMeier提到的基于索引的循环.抱歉,我不知道Ruby,所以下面的示例是在C#中!希望你不介意:-)

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

我修改了以下代码,通过引用比较而不是值(只能使用引用类型而不是值类型).以下代码应该支持包含相同字符串(但不是相同的字符串对象)的多个对象,因为MattChurcy的示例没有指定字符串必须是不同的,我使用LINQ Last方法而不是计算索引.

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

上述代码的局限性.(1)它只适用于字符串或引用类型而不是值类型.(2)同一对象只能在列表中出现一次.您可以拥有包含相同内容的不同对象.文字字符串不能重复使用,因为C#不会为具有相同内容的字符串创建唯一对象.

我也不傻.我知道基于索引的循环是要使用的循环.当我第一次发布初步答案时,我已经说过了.我在问题的背景下提供了最好的答案.我太累了,不能继续解释这个,所以你们都投票删除我的答案.如果这个消失,我会很高兴的.谢谢

  • 这是一个容易出错的代码.您实际上按值而不是索引进行比较.如果有一个对象等于最后一个,你将有几个"lastone:"行. (46认同)
  • C#3.0也将在阵列上有.Last运算符 (4认同)
  • @Kirchstein:如果集合没有实现IList <T>,那么调用Last方法将涉及评估整个序列,因此性能可能很差.如果集合确实实现了IList <T>,那么您可以像上面的TomatoGG那样执行类似"var lastItem = list [list.Count - 1]"的操作. (3认同)
  • 哇这个stackoverflow的事情真的很难.我真的不知道如何处理一个问题.我是否应该根据提问者想要提供的解决方案,或者通过拒绝提供不符合标准的答案来成为标准和最佳实践的传播者? (2认同)
  • @TomatoGG:要使其正常工作,您需要按索引查找最后一项,*不*按值查找,*不*按引用查找.这就是为什么如果索引无法访问集合,使用"foreach"并不能真正获得优雅的解决方案,并且如果集合可以通过索引访问,那么简单的"for"循环将提供比"foreach"更优雅的解决方案. (2认同)

kgi*_*kis 22

这够优雅吗?它假定一个非空列表.

  list[0,list.length-1].each{|i|
    puts "Looping:"+i # if not last loop iteration
  }
  puts "Last one:" + list[list.length-1]
Run Code Online (Sandbox Code Playgroud)

  • 尼斯.您也可以分别使用list [0 ...- 1]和list [-1]而不是list [0,list.length-1]和list [list.length-1]. (4认同)

Dav*_*ows 13

在Ruby中我会each_with_index在这种情况下使用

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
}
Run Code Online (Sandbox Code Playgroud)


Sva*_*nte 9

您可以eachwithlast在类中定义一个方法,使其与each除最后一个元素之外的所有元素相同,但最后一个方法则为其他元素:

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

然后你可以像这样调用它(foo作为其实例MyColl或子类):

foo.eachwithlast do |value, last|
  if last
    puts "Last one: "+value
  else
    puts "Looping: "+value
  end
end
Run Code Online (Sandbox Code Playgroud)

编辑:遵循molf的建议:

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 } )
Run Code Online (Sandbox Code Playgroud)


boh*_*nko 5

C#3.0或更新版本

首先,我会写一个扩展方法:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用新foreach的很简单:

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

void Run()
{
    lData.ForEachEx((el, last) =>
    {
        if (last)
            Console.Write("last one: ");
        Console.WriteLine(el);
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 很优雅,喜欢!但是,我更喜欢 'while(!last)' 而不是显式中断......并且 'last' 需要初始化 (2认同)