2009-02-06 5 views
123

Есть ли эквивалент C# для enumerate() Python и Ruby's each_with_index?foreach с индексом

+0

Если вы используете LINQ, есть переопределения различных функций, которые допускают перечисление. В противном случае вы обычно застреваете, используя переменную, которую вы увеличиваете. – GWLlosa

+0

Можем ли мы обновить этот вопрос до C# 7.0, где теперь есть кортежи? Интересно, как будет выглядеть решение с использованием кортежей. –

+0

Является ли это особенностью 'foreach', что вы можете обрабатывать каждый элемент, который полностью отделен от его позиции в списке? –

ответ

48

C# foreach не имеет встроенного индекса. Вам нужно будет добавить целое число вне цикла foreach и увеличивать его каждый раз.

int i = -1; 
foreach (Widget w in widgets) 
{ 
    i++; 
    // do something 
} 

В качестве альтернативы, вы можете использовать стандартный цикл следующим образом:

for (int i = 0; i < widgets.Length; i++) 
{ 
    w = widgets[i]; 
    // do something 
} 
+14

Я думаю, вы должны инициализировать i до -1 и увеличивать его в начале тела цикла, чтобы убедиться, что оператор «continue» не вызывает проблем. –

+6

@DrJokepu почему бы не просто сохранить i = 0 и увеличить его до закрывающей скобки инструкции foreach? Инициализация с чем-то! = 0 вызывает внимание, когда вы позже просматриваете код ... – Adi

0

Это зависит от класса, который вы используете.

словарь < (Из < (TKey, TValue>)>) Класс Для примера поддержка Этот

Словарь < (Из < (TKey, TValue>)>) общий класс обеспечивает отображение из множества ключей к набору значений.

Для целей перечисления каждый элемент в словаре рассматривается как структура KeyValuePair < (Of < (TKey, TValue>)>), представляющая значение и его ключ. Порядок, в котором возвращаются элементы, не определен.

Еогеасп (KeyValuePair КВП в MyDictionary) {...}

171

Вы можете сделать следующее

foreach (var it in someCollection.Select((x,i) => new { Value = x, Index=i })) 
{ 
    if (it.Index > SomeNumber) //  
} 

Это позволит создать анонимное значение типа для каждой записи в сборном. Она будет иметь два свойства

  1. Value: с исходным значением в коллекции
  2. Индекса: с индексом в коллекции
+11

Умный, но это похоже на почесывание левого уха правой рукой. Думаю, я просто буду держать индекс сам, поэтому я не путаю будущих сопровождающих. – Ken

+1

A для усилий, но F для общей читаемости и обслуживания. Извините. –

+31

@Neil, я поражен тем, что люди думают, что это проблема обеспечения. Эта перегрузка Select (и других методов LINQ) была предоставлена ​​для полного Цель таких операций. – JaredPar

15

Помимо ответов LINQ уже данных, у меня есть "SmartEnumerable" класса который позволяет получить индекс и «первый/последний». Это немного уродливо с точки зрения синтаксиса, но вы можете найти его полезным.

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

+2

Отлично! Эти небольшие вспомогательные свойства (первый/последний/индекс) должны быть включены в стандартную инфраструктуру .net! –

+0

Замечательно, мне просто не нравится его длинное и не описательное имя. –

6

Мое решение включает в себя простой класс Pair I, созданный для общей утилиты, и который по сути является тем же самым, что и класс framework KeyValuePair. Затем я создал пару функций расширения для IEnumerable, называемых Ordinate (из термина теории множеств «ordinal»).

Эти функции возвращают для каждого элемента объект Pair, содержащий индекс, и сам элемент.

public static IEnumerable<Pair<Int32, X>> Ordinate<X>(this IEnumerable<X> lhs) 
{ 
    return lhs.Ordinate(0); 
} 

public static IEnumerable<Pair<Int32, X>> Ordinate<X>(this IEnumerable<X> lhs, Int32 initial) 
{ 
    Int32 index = initial - 1; 

    return lhs.Select(x => new Pair<Int32, X>(++index, x)); 
} 
+1

Обратите внимание, что в наши дни существует 'System.Tuple' для вашей' Pair'. – Ashe

3

Нет, не существует.

Как показали другие люди, существуют способы имитировать поведение Руби. Но возможно иметь тип, который реализует IEnumerable, который не предоставляет индекс.

241

Я держу этот метод расширения вокруг этого:

public static void Each<T>(this IEnumerable<T> ie, Action<T, int> action) 
{ 
    var i = 0; 
    foreach (var e in ie) action(e, i++); 
} 

И использовать его так:

var strings = new List<string>(); 
strings.Each((str, n) => 
{ 
    // hooray 
}); 

Или позволить break -как поведение:

public static bool Each<T>(this IEnumerable<T> ie, Func<T, int, bool> action) 
{ 
    int i = 0; 
    foreach (T e in ie) if(!action(e, i++)) return false; 
    return true; 
} 

var strings = new List<string>() { "a", "b", "c" }; 
bool iteratedAll = strings.Each((str, n)) => 
{ 
    if(str == "b") return false; 
    return true; 
}); 
+6

Это лучший ответ здесь. Я был удивлен, что нужно прокрутить вниз, чтобы увидеть это. –

+27

Вы не можете сломать этот цикл foreach. –

+18

Это потому, что это не петля foreach. –

2

I просто вычислил интересное решение:

public class DepthAware<T> : IEnumerable<T> 
{ 
    private readonly IEnumerable<T> source; 

    public DepthAware(IEnumerable<T> source) 
    { 
     this.source = source; 
     this.Depth = 0; 
    } 

    public int Depth { get; private set; } 

    private IEnumerable<T> GetItems() 
    { 
     foreach (var item in source) 
     { 
      yield return item; 
      ++this.Depth; 
     } 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return GetItems().GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

// Generic type leverage and extension invoking 
public static class DepthAware 
{ 
    public static DepthAware<T> AsDepthAware<T>(this IEnumerable<T> source) 
    { 
     return new DepthAware<T>(source); 
    } 

    public static DepthAware<T> New<T>(IEnumerable<T> source) 
    { 
     return new DepthAware<T>(source); 
    } 
} 

Использование:

var chars = new[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}.AsDepthAware(); 

foreach (var item in chars) 
{ 
    Console.WriteLine("Char: {0}, depth: {1}", item, chars.Depth); 
} 
+2

Воскрешение зомби с тех пор, как я просто нуждался в этом: это будет вести себя смутно, если перечислимое будет повторяться несколько раз. – millimoose

2

Это ваша коллекция

var values = new[] {6, 2, 8, 45, 9, 3, 0}; 

Сделать выбор показателей для этой коллекции

var indexes = Enumerable.Range(0, values.Length).ToList(); 

Используйте диапазон для итерации с индексом

indexes.ForEach(i => values[i] += i); 
indexes.ForEach(i => Console.Write("[{0}] = {1}", i, values[i])); 
14

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

public struct EnumeratedInstance<T> 
{ 
    public long cnt; 
    public T item; 
} 

public static IEnumerable<EnumeratedInstance<T>> Enumerate<T>(this IEnumerable<T> collection) 
{ 
    long counter = 0; 
    foreach (var item in collection) 
    { 
     yield return new EnumeratedInstance<T> 
     { 
      cnt = counter, 
      item = item 
     }; 
     counter++; 
    } 
} 

и пример использования:

foreach (var ii in new string[] { "a", "b", "c" }.Enumerate()) 
{ 
    Console.WriteLine(ii.item + ii.cnt); 
} 

Одна хорошая вещь, что если вы привыкли к синтаксису Python вы все равно можете использовать его:

foreach (var ii in Enumerate(new string[] { "a", "b", "c" })) 
+0

Какая часть этого последнего бита выглядит как синтаксис Python для вас? – ArtOfWarfare

+0

Извините за некропость. Я модифицировал вашу функцию, чтобы она выглядела красивой и более питонической: http://pastebin.com/aExvenyY – KgOfHedgehogs