IEnumerable foreach, do something different for the last element

后端 未结 5 2216
庸人自扰
庸人自扰 2021-02-20 06:23

I have an IEnumerable. I want to do one thing for each item of the collection, except the last item, to which I want to do something else. How can I code this neatly? I

相关标签:
5条回答
  • 2021-02-20 06:30

    Similar to Marc's answer, but you could write an extension method to wrap it up.

    public static class LastEnumerator
    {
        public static IEnumerable<MetaEnumerableItem<T>> GetLastEnumerable<T>(this IEnumerable<T> blah)
        {
            bool isFirst = true;
            using (var enumerator = blah.GetEnumerator())
            {
                if (enumerator.MoveNext())
                {
                    bool isLast;
                    do
                    {
                        var current = enumerator.Current;
                        isLast = !enumerator.MoveNext();
                        yield return new MetaEnumerableItem<T>
                            {
                                Value = current,
                                IsLast = isLast,
                                IsFirst = isFirst
                            };
                        isFirst = false;
                    } while (!isLast);
                }
            }
    
        }
    }
    
    public class MetaEnumerableItem<T>
    {
        public T Value { get; set; }
        public bool IsLast { get; set; }
        public bool IsFirst { get; set; }
    }
    

    Then call it like so:

    foreach (var row in records.GetLastEnumerable())
    {
        output(row.Value);
        if(row.IsLast)
        {
            outputLastStuff(row.Value);
        }
    }
    
    0 讨论(0)
  • 2021-02-20 06:31

    not 100% sure I like this but you could always delay the use of item until you've moved one step in to the IEnumerable array, that way when you get to the end you've not used the last entry.

    it avoids having to force a count on the enumerator.

    object item = null;
    foreach (var a in items)
    {
      // if item is set then we can use it.
      if (item != null)
      {
          // not final item
          f(item);
      }
      item = a;
    }
    
    // final item.
    g(item);
    
    0 讨论(0)
  • 2021-02-20 06:43

    Since you mention IEnumerable[<T>] (not IList[<T>] etc), we can't rely on counts etc: so I would be tempted to unroll the foreach:

    using(var iter = source.GetEnumerator()) {
        if(iter.MoveNext()) {
            T last = iter.Current;
            while(iter.MoveNext()) {
                // here, "last" is a non-final value; do something with "last"
                last = iter.Current;
            }
            // here, "last" is the FINAL one; do something else with "last"
        }
    }
    

    Note the above is technically only valid for IEnuemerable<T>; for non-generic, you'd need:

    var iter = source.GetEnumerator();
    using(iter as IDisposable) {
        if(iter.MoveNext()) {
            SomeType last = (SomeType) iter.Current;
            while(iter.MoveNext()) {
                // here, "last" is a non-final value; do something with "last"
                last = (SomeType) iter.Current;
            }
            // here, "last" is the FINAL one; do something else with "last"
        }
    }
    
    0 讨论(0)
  • 2021-02-20 06:45

    If you want to do this as efficiently as possible there is no other choice than effectively looking at not only the current but also the "next" or "previous" item, so you can defer the decision of what to do after you have that information. For example, assuming T is the type of items in the collection:

    if (collection.Any()) {
        var seenFirst = false;
        T prev = default(T);
        foreach (var current in collection) {
            if (seenFirst) Foo(prev);
            seenFirst = true;
            prev = current;
        }
        Bar(prev);
    }
    

    See it in action.

    0 讨论(0)
  • 2021-02-20 06:51

    I wouldn't really recommend it, but I guess you could do something like this...

    object m_item = notPartOfListFlag = new object();
    foreach(var item in enumerator){
       if(m_item != notPartOfListFlag)
       {
          //do stuff to m_item;
       }
       m_item = item;
    }
    //do stuff to last item aka m_item;
    

    But I would try to use some kind of collection that exposes the position of the items in the list, then use

    if(collection.IndexOf(item) == collection.Count-1) do stuff
    
    0 讨论(0)
提交回复
热议问题