Как я узнаю, что я перебираю последний элемент коллекции? - PullRequest
5 голосов
/ 26 декабря 2010

Я хочу сделать что-то другое на последнем KeyValuePair из Dictionary, который я повторяю.

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

Возможно ли это?

ПовторноИзменить: Я не использую значения словаря, на самом деле я использую список KeyValuePair с.Я преобразовал их и не заметил позже, тупой.

Ответы [ 9 ]

8 голосов
/ 26 декабря 2010

Я бы сделал ваш собственный метод расширения.Данная реализация гарантирует, что вы пройдете только один раз.

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
8 голосов
/ 26 декабря 2010

Если вы не хотите реализовать foreach самостоятельно, используйте счетчик (код C #):

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

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

Обратите внимание, что это будет работать только с не потоковыми IEnumerable<T>, также, в зависимости от реализации, Count приведет к тому, что коллекция будет проходить дважды.

7 голосов
/ 03 декабря 2012

Почему бы не использовать:

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
    }
}
3 голосов
/ 26 декабря 2010

Преобразование коллекции в список (например, в LINQ ToList ()) и итерация с циклом for (int i = 0) ....

2 голосов
/ 26 декабря 2010

У меня была идея, что счетчик не используется.

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

Редактировать: извините, это для списка, я преобразовал его раньше, и intellisense дал мне методы списка вместо словарей.

2 голосов
/ 26 декабря 2010

Вы не можете сделать это естественным образом с 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, чтобы получить значение текущего элемента.

Хотя есть одно замечание по поводу перебора словарей - на самом деле они не являютсяв любом порядке.Таким образом, хотя вы можете выполнять итерацию, и будет первой и последней записью, вы не должны предполагать, что это будет первая добавленная запись и последняя добавленная запись соответственно.Это может не быть проблемой для вашего варианта использования, но вы должны знать об этом.

1 голос
/ 06 сентября 2012

Я создал решение для построения IP-строки из 4 октетов на основе ответа @CamiloMartin, приведенного выше.

 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();
1 голос
/ 26 декабря 2010
        foreach (var item in collection.Reverse().Skip(1))
        {

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

Другой подход заключается в поддержании индекса итерации, и когда вы нажмете collection.Length-2 или collection.Count()-2, остановите цикл и сделайте все, что вы хотите с последним элементом.

Но, как сказал @BrokenGlass, остерегайтесь побочных эффектов, которые могут возникнуть в вашем приложении.

И, как правило, вы должны использовать Linq с особой осторожностью. Например, каждый раз, когда вы выполняете итерацию в коллекции linq to sql, вы снова выполняете запрос, поэтому гораздо лучше выполнить его один раз с нетерпением , просто вызвав .ToList() и использовать его в коллекции памяти столько, сколько вам нужно. хочу.

0 голосов
/ 26 декабря 2010

С помощью словаря у вас есть несколько вариантов

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 и либо использовать счетчик, либо выполнить сравнение 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
...