如何检查一个实体是否是 foreach 循环的第一个元素?

64

假设我有一个foreach循环。

我必须对循环的第一个对象执行某些操作,而其他对象则不需要这样做。

如何检查当前在循环中的项是否为第一个对象。

10个回答

114

我喜欢使用Linq的方式,但是不使用Skip(1)的话,你也可以用它来获取列表中的最后一个项目,并且你的代码仍然保持简洁。

foreach(var item in items)
{
    if (items.First()==item)
        item.firstStuff();

    else if (items.Last() == item)
        item.lastStuff();

    item.otherStuff();
}

15
我不喜欢这种方法,因为它不是严格准确的。比如,如果一个项目出现超过一次,并且还是第一个或最后一个项目,那么第一个或最后一个条件就会被触发多次。或者如果某些项目相等,它们也可能会被多次触发。 - Dave Cousineau
3
这个问题是关于测试项目是否为第一个的。如果你清除了else if部分,也就是说你只测试该项是否为第一个,那么你的合理担忧将得到缓解。 - ErTR

63

你可以采取以下几种方式来实现。

  1. 使用 for 循环替代。
  2. 设置布尔标志。
  3. 使用 Linq 获取 list.First(),然后在 list.Skip(1) 上使用 foreach

40

像这样:

bool first = true;

foreach(var item in items)
{
    if (first)
    {
        item.firstStuff();
        first = false;
    }
    item.otherStuff();
}

20

以下是高效的解决方案:

using (var erator = enumerable.GetEnumerator())
{
    if (erator.MoveNext())
    {
        DoActionOnFirst(erator.Current);

        while (erator.MoveNext())
            DoActionOnOther(erator.Current);
    }
}

编辑:这里还有一个 LINQ 的:

if (enumerable.Any())
    {
        DoActionOnFirst(enumerable.First());

        foreach (var item in enumerable.Skip(1))
            DoActionOnOther(item);
    }

编辑:如果对于项目的操作具有可分配给 Func<TItem, TResult> 的签名,则可以执行以下操作:

enumerable.Select((item, index) => index == 0 ? GetResultFromFirstItem(item) : GetResultFromOtherItem(item));

5
虽然这在技术上满足要求,但使用“first”标记来指示,如其他人所发表的那样,会更易读。 - Paul Alexander
4
@ Paul:更有效率,我不认为它会显著降低可读性。 - Ani
不确定与使用“first”标志相比,这种方法如何更有效率。 - Babar
7
使用这个解决方案,每次迭代无需评估变量。 - Lukazoid
对于LINQ答案,这应该是正确的答案,因为它比使用标志更优雅。 - bytedev

6
bool first = true;
foreach(var foo in bar)
{
  if (first)
  {
    // do something to your first item
    first = false;
  }
  // do something else to the rest
}

太慢了!没看到geofftnz的帖子。 - Hamza
3
这是StackOverflow专利的简单问题竞赛 :) - geofftnz

6
在我看来,这是最简单的方法。
foreach (var item in list)
{
    if((list.IndexOf(item) == 0)
    {
        // first
    }
    // others
}

8
可能这是最简单的方式,但它非常昂贵。如果列表是任意的“IEnumerable”,则复杂度在O(n)范围内,每次迭代都需要执行很多开销。 - Pretasoc

3

试试这个

bool IsFirst = true;

foreach(DataRow dr in dt.Rows)
{
    if (IsFirst)
    {
        // do some thing
        IsFirst = false;
    }    
}

2

我除了这个之外什么都想不到。

var processedFirst = false;
foreach(var x in items) {
    if(!processedFirst) {
        ProcessFirst(x);
        processedFirst = true;
    }

2

这是一个通用的解决方案,可以获取数组中每个对象的索引。如果需要测试是否为第一个,应该能够正常工作。

        List<String> entries = new List<string>();
        entries.Add("zero");
        entries.Add("one");
        entries.Add("two");

        Dictionary<int, String> numberedEntries = new Dictionary<int, string>();
        int i = 0;
        entries.ForEach(x => numberedEntries.Add(i++, x));
        foreach (KeyValuePair<int, String> pair in numberedEntries) {
            Console.WriteLine(pair.Key + ": " + pair.Value);
        }

在这种设置中,KeyValuePair的Key是索引,而值是该索引处的对象,在我的示例中是一个字符串,但任何对象都可以放在那里。它会增加一些额外的开销,但可以在需要时确定列表中任何对象的索引。

闻起来像是Python的dict.items我喜欢它! - Hovis Biddle

1

您也可以这样写:

foreach ((T item, bool isFirst) in items.Select((item, index) => (item, index == 0)))
{
    if (isFirst)
    {
        ...
    }
    else
    {
        ...
    }
}

如果您需要频繁使用此功能,可以编写扩展方法来替换Select并使您的代码更短。

我在寻找很久以前使用过的类型 IndexedEnumerable。https://gist.github.com/jeremysimmons/545de72e0447c55b9ae5ede910d5129b。不过,我确实很欣赏您提供的简短版本。 - JJS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接