Как вы получаете индекс текущей итерации цикла foreach? - PullRequest
789 голосов
/ 04 сентября 2008

Есть ли какая-нибудь редкая языковая конструкция, с которой я не сталкивался (например, немногие, которые я недавно узнал, некоторые о переполнении стека) в C # для получения значения, представляющего текущую итерацию цикла foreach?

Например, в настоящее время я делаю что-то подобное в зависимости от обстоятельств:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}

Ответы [ 34 ]

3 голосов
/ 11 сентября 2010

Мое решение этой проблемы - метод расширения WithIndex(),

http://code.google.com/p/ub-dotnet-utilities/source/browse/trunk/Src/Utilities/Extensions/EnumerableExtensions.cs

Используйте это как

var list = new List<int> { 1, 2, 3, 4, 5, 6 };    

var odd = list.WithIndex().Where(i => (i.Item & 1) == 1);
CollectionAssert.AreEqual(new[] { 0, 2, 4 }, odd.Select(i => i.Index));
CollectionAssert.AreEqual(new[] { 1, 3, 5 }, odd.Select(i => i.Item));
3 голосов
/ 23 августа 2016

Вы можете написать свой цикл так:

var s = "ABCDEFG";
foreach (var item in s.GetEnumeratorWithIndex())
{
    System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
}

После добавления следующей структуры и метода расширения.

Метод struct и extension инкапсулирует функциональность Enumerable.Select.

public struct ValueWithIndex<T>
{
    public readonly T Value;
    public readonly int Index;

    public ValueWithIndex(T value, int index)
    {
        this.Value = value;
        this.Index = index;
    }

    public static ValueWithIndex<T> Create(T value, int index)
    {
        return new ValueWithIndex<T>(value, index);
    }
}

public static class ExtensionMethods
{
    public static IEnumerable<ValueWithIndex<T>> GetEnumeratorWithIndex<T>(this IEnumerable<T> enumerable)
    {
        return enumerable.Select(ValueWithIndex<T>.Create);
    }
}
3 голосов
/ 19 апреля 2011

Для интереса, Фил Хаак только что написал пример этого в контексте делегата с шаблонами Razor (http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx)

По сути, он пишет метод расширения, который оборачивает итерацию в класс «IteratedItem» (см. Ниже), предоставляя доступ к индексу, а также к элементу во время итерации.

public class IndexedItem<TModel> {
  public IndexedItem(int index, TModel item) {
    Index = index;
    Item = item;
  }

  public int Index { get; private set; }
  public TModel Item { get; private set; }
}

Однако, хотя это было бы хорошо в среде без Razor, если вы выполняете одну операцию (то есть ту, которая может быть предоставлена ​​как лямбда), это не будет надежной заменой синтаксиса for / foreach в non -Раззор контекстов.

3 голосов
/ 25 октября 2013

Я построил это в LINQPad :

var listOfNames = new List<string>(){"John","Steve","Anna","Chris"};

var listCount = listOfNames.Count;

var NamesWithCommas = string.Empty;

foreach (var element in listOfNames)
{
    NamesWithCommas += element;
    if(listOfNames.IndexOf(element) != listCount -1)
    {
        NamesWithCommas += ", ";
    }
}

NamesWithCommas.Dump();  //LINQPad method to write to console.

Вы также можете просто использовать string.join:

var joinResult = string.Join(",", listOfNames);
3 голосов
/ 22 марта 2013

Не думаю, что это должно быть достаточно эффективно, но это работает:

@foreach (var banner in Model.MainBanners) {
    @Model.MainBanners.IndexOf(banner)
}
2 голосов
/ 23 марта 2011

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

Это может быть довольно распространенным случаем, в основном, я читаю из одного списка источников и создаю объекты на их основе в списке адресатов, однако мне нужно сначала проверить, действительны ли исходные элементы, и хочу вернуть строка любой ошибки. На первый взгляд, я хочу получить индекс в перечислителе объекта в свойстве Current, однако, поскольку я копирую эти элементы, я в любом случае неявно знаю текущий индекс из текущего места назначения. Очевидно, это зависит от вашего целевого объекта, но для меня это был список, и, скорее всего, он будет реализовывать ICollection.

т.е.

var destinationList = new List<someObject>();
foreach (var item in itemList)
{
  var stringArray = item.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

  if (stringArray.Length != 2)
  {
    //use the destinationList Count property to give us the index into the stringArray list
    throw new Exception("Item at row " + (destinationList.Count + 1) + " has a problem.");
  }
  else
  {
    destinationList.Add(new someObject() { Prop1 = stringArray[0], Prop2 = stringArray[1]});
  }
}

Не всегда применимо, но, по-моему, достаточно часто, чтобы упоминать о нем.

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

2 голосов
/ 29 октября 2010

Как насчет этого? Обратите внимание, что myDelimitedString может быть нулевым, если myEnumerable пуст.

IEnumerator enumerator = myEnumerable.GetEnumerator();
string myDelimitedString;
string current = null;

if( enumerator.MoveNext() )
    current = (string)enumerator.Current;

while( null != current)
{
    current = (string)enumerator.Current; }

    myDelimitedString += current;

    if( enumerator.MoveNext() )
        myDelimitedString += DELIMITER;
    else
        break;
}
2 голосов
/ 14 сентября 2011

Я не был уверен, что вы пытаетесь сделать с индексной информацией, основанной на вопросе. Однако в C # обычно можно адаптировать метод IEnumerable.Select, чтобы получить индекс из всего, что вы хотите. Например, я мог бы использовать что-то подобное для определения, является ли значение нечетным или четным.

string[] names = { "one", "two", "three" };
var oddOrEvenByName = names
    .Select((name, index) => new KeyValuePair<string, int>(name, index % 2))
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

Это даст вам словарь по имени того, был ли элемент нечетным (1) или четным (0) в списке.

2 голосов
/ 04 сентября 2008

Я не верю, что есть способ получить значение текущей итерации цикла foreach. Подсчет себя, кажется, лучший способ.

Могу я спросить, почему вы хотели бы знать?

Похоже, вам больше всего хотелось бы сделать одну из трех вещей:

1) Получение объекта из коллекции, но в этом случае он у вас уже есть.

2) Подсчет объектов для последующей постобработки ... коллекции имеют свойство Count, которое вы можете использовать.

3) Установка свойства объекта на основе его порядка в цикле ... хотя вы могли бы легко установить это при добавлении объекта в коллекцию.

2 голосов
/ 04 сентября 2008

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

Однако при работе с индексами единственным разумным ответом на проблему является использование цикла for. Все остальное представляет сложность кода, не говоря уже о сложности времени и пространства.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...