Вопрос по c#, .net – IEnumerable foreach, сделайте что-то другое для последнего элемента

11

у меня естьIEnumerable<T>, Я хочу сделать одну вещь для каждого элемента коллекции, кроме последнего, с которым я хочу сделать что-то еще. Как я могу аккуратно закодировать это? В псевдокоде

foreach (var item in collection)
{
    if ( final )
    {
        g(item)
    }
    else
    {
        f(item)
    }
}

Так что если бы мои IEnumerable былиEnumerable.Range(1,4) Я делаю f (1) f (2) f (3) g (4). NB. Если мой IEnumerable имеет длину 1, я хочу g (1).

Мой IEnumerable бывает отчасти дерьмовым, что делаетCount() так дорого, как перебирать все это.

Ваш Ответ

5   ответов
0

Я бы не стал этого рекомендовать, но думаю, вы могли бы сделать что-то подобное ...

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;

Но я бы попробовал использовать какую-то коллекцию, которая выставляет позиции элементов в списке, а затем использую

if(collection.IndexOf(item) == collection.Count-1) do stuff
Error: User Rate Limit Exceeded
nullError: User Rate Limit Exceeded
21

Поскольку вы упоминаетеIEnumerable[<T>] (неIList[<T>] и т. д.), мы не можем полагаться на счет и т. д., поэтому я бы соблазнился развернуть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"
    }
}

Обратите внимание, что вышеtechnically действует только дляIEnuemerable<T>; для неуниверсальных вам необходимо:

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"
    }
}
IEnuemerableError: User Rate Limit ExceededIEnumerableError: User Rate Limit Exceeded
Error: User Rate Limit Exceededgist.github.com/2781446 Colonel Panic
Error: User Rate Limit ExceededfinalError: User Rate Limit ExceededlastError: User Rate Limit ExceededpreviousError: User Rate Limit ExceedednotError: User Rate Limit Exceeded
2

Аналогично ответу Марка, но вы можете написать метод расширения, чтобы обернуть его.

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

Тогда назовите это так:

foreach (var row in records.GetLastEnumerable())
{
    output(row.Value);
    if(row.IsLast)
    {
        outputLastStuff(row.Value);
    }
}
1

Если вы хотите сделать это максимально эффективно, то нет другого выбора, кроме как эффективно смотреть не только на текущий, но и на «следующий». или "предыдущий" пункт, так что вы можете отложить решение, что делать после того, как у вас есть эта информация. Например, предполагаяT это тип предметов в коллекции:

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

Увидеть это в действии.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
0

не уверен на 100%, что мне это нравится, но вы всегда можете отложить использование элемента до тех пор, пока вы не переместитесь на один шаг к массиву IEnumerable, то есть, когда вы дойдете до конца, вы не использовали последнюю запись.

это избавляет от необходимости навязывать счетчик.

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);
Error: User Rate Limit Exceeded
nullError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

Похожие вопросы