繁体   English   中英

我怎么知道我是否正在迭代集合的最后一项?

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

我想在我正在迭代的Dictionary的最后一个KeyValuePair上做一些不同的事情。

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

这可能吗?

重新编辑:我没有使用字典值,我实际上使用的是KeyValuePair列表。 我转换了它们,后来没有注意到,愚蠢的我。

除非您想自己实现foreach ,否则请使用计数器(C#代码):

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

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

请注意,这只适用于非流式IEnumerable<T> ,同样,根据实现, Count会导致集合走两次。

我会制作你自己的扩展方法。 这里的实现保证您只需走一次集合。

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);
        }
    }
}    

用法:

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

输出:

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

为什么不使用:

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
    }
}

将集合转换为列表(如LINQ的ToList())并使用for(int i = 0)...循环进行迭代。

你不能用IEnumerable<T>自然地做到这一点,因为在你试图进入下一个元素之前你不会知道是否还有其他元素。

但是,在我的MiscUtil库中,我有一个名为SmartEnumerable东西,它预先读取一个项目,以便提供以下属性:

  • IsFirst
  • IsLast
  • Index
  • Value

有关使用说明,请参阅此页面 例如:

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

您需要使用item.Value来获取当前项的值。

关于迭代字典的一点是 - 它们实际上并不是任何顺序。 因此,虽然您可以迭代并且有第一个条目和最后一个条目,但您不应该假设它们将是添加的第一个条目和分别添加的最后一个条目。 对于您的用例而言,这可能不是问题,但您应该意识到这一点。

我只是有一个不使用计数器的想法。

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

编辑:对不起,这是一个List,我之前已经转换过它,intellisense给了我List方法而不是Dictionary方法。

我根据上面的@CamiloMartin答案创建了一个从4个八位字节构建IP字符串的解决方案。

 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();
        foreach (var item in collection.Reverse().Skip(1))
        {

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

另一种方法是维护迭代索引,当命中collection.Length-2collection.Count()-2然后停止循环并使用最后一个元素执行任何操作。

但正如@BrokenGlass所说,要注意它可能对你的应用程序产生的副作用。

一般来说,你必须特别小心使用Linq。 例如每次你在LINQ to SQL的访问集合的时候,你再执行查询,所以更好的做法是通过简单地调用一次急切地运行它.ToList()和使用,在内存中的集合,只要你想尽可能多。

使用词典,您有多种选择

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

我建议你对ValueCollection进行itterate并使用一个计数器或在当前和最后一项之间进行Equals比较(这可能会慢一点,但你保存一个计数器变量;)不要忘记添加一个空的检查名单。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM