Как получить индекс элемента в списке за один шаг?

193

Как найти индекс элемента в списке, не просматривая его?

В настоящее время это выглядит не очень хорошо - поиск по списку одного и того же элемента дважды, просто чтобы получить индекс:

var oProp = something;

int theThingIActuallyAmInterestedIn = myList.IndexOf(myList.Single(i => i.Prop == oProp));
Даниэль Робинсон
источник

Ответы:

435

Как насчет метода List.FindIndex :

int index = myList.FindIndex(a => a.Prop == oProp);

Этот метод выполняет линейный поиск; следовательно, этот метод является операцией O (n), где n является Count.

Если элемент не найден, он вернет -1

Алекс Филиповичи
источник
2
Как насчет int index?
Дилан Ченски
2
@DylanChensky он слишком много кодирует JS
lennyy
9
Для справки, если предмет не найден; он вернется -1
Даниэль Филипе
103

Для простых типов вы можете использовать «IndexOf»:

List<string> arr = new List<string>();
arr.Add("aaa");
arr.Add("bbb");
arr.Add("ccc");
int i = arr.IndexOf("bbb"); // RETURNS 1.
Хосе Мануэль Абарка Родригес
источник
71

РЕДАКТИРОВАТЬ: Если вы используете толькоList<> и вам нужен только индекс, то List.FindIndexэто действительно лучший подход. Я оставлю этот ответ здесь для тех, кому нужно что-то другое (например, поверх любого IEnumerable<>).

Используйте перегрузку Selectкоторого занимает индекс в предикате, поэтому вы преобразуете свой список в пару (индекс, значение):

var pair = myList.Select((Value, Index) => new { Value, Index })
                 .Single(p => p.Value.Prop == oProp);

Затем:

Console.WriteLine("Index:{0}; Value: {1}", pair.Index, pair.Value);

Или, если вам нужен только индекс, и вы используете его в нескольких местах, вы можете легко написать свой собственный метод расширения, который был похож Where, но вместо возврата исходных элементов он возвращал индексы тех элементов, которые соответствовали предикату.

Джон Скит
источник
Кажется, он хочет только индекс. List <>. FindIndex (Predicate <>) - лучший подход. Хотя заголовок вопроса намекает иначе, описание ОП довольно ясно, ему нужен только индекс «int theThingIActuallyAmInterestedIn»
Луи Риччи
1
@LastCoder: Ага - пропустил FindIndex. Да, я полностью согласен.
Джон Скит
Просто чтобы прояснить, является ли «индекс / значение -> одиночный» подход «лучше» (здесь означает, что он быстрее в терминах Big-O), чем повторение вручную дважды? Или поставщик LINQ2Objects достаточно умен, чтобы оптимизировать одну из итераций? (Я делаю предположение, что как Select, так и Single, вообще говоря, являются O (n) операциями)
sara
1
@kai: Я думаю, вам нужно прочитать о том, как работает LINQ. Это слишком сложно объяснить подробно в комментарии. Однако ... это только одна итерация по исходной коллекции. LINQ устанавливает конвейер, который лениво преобразует входную последовательность в другую последовательность, а затем Single()операция выполняет итерацию по этой последовательности и находит единственный элемент, который соответствует предикату. Для получения более подробной информации прочитайте мою серию блогов edulinq: codeblog.jonskeet.uk/category/edulinq
Джон Скит
1
+1 Мне нужно было это решение. Босс подумал, что я умный на этот раз. Мне посоветовали тщательно задокументировать это, поскольку он использовал анонимный тип и может быть не понятен следующему кодировщику в этой области.
Адам Уэллс
14

Если вы не хотите использовать LINQ, то:

int index;
for (int i = 0; i < myList.Count; i++)
{
    if (myList[i].Prop == oProp)
    {
       index = i;
       break;
    }
}

Таким образом, вы повторяете список только один раз.

gzaxx
источник
22
@ KingKing никто не сказал, что это так.
Томер W
1
Это та же реализация, что и в Linq FindIndex, из интереса?
Coops
2
вероятно, не тот же код, List имеет некоторые аккуратные оптимизации здесь и там. но мне трудно поверить, что они могут искать неупорядоченный список менее чем за O (n), поэтому я бы сказал, что они, вероятно, действительно похожи на практике.
Сара
6
  1. Простое решение для поиска индекса для любого строкового значения в списке.

Вот код для списка строк:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Простое решение для поиска индекса для любого значения Integer в списке.

Вот код для списка целых чисел:

    int indexOfNumber = myList.IndexOf(/*insert number from list*/);

источник
2

Вот способ расширения для копирования / вставки для IEnumerable

public static class EnumerableExtensions
{
    /// <summary>
    /// Searches for an element that matches the conditions defined by the specified predicate,
    /// and returns the zero-based index of the first occurrence within the entire <see cref="IEnumerable{T}"/>.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="list">The list.</param>
    /// <param name="predicate">The predicate.</param>
    /// <returns>
    /// The zero-based index of the first occurrence of an element that matches the conditions defined by <paramref name="predicate"/>, if found; otherwise it'll throw.
    /// </returns>
    public static int FindIndex<T>(this IEnumerable<T> list, Func<T, bool> predicate)
    {
        var idx = list.Select((value, index) => new {value, index}).Where(x => predicate(x.value)).Select(x => x.index).First();
        return idx;
    }
}

Наслаждаться.

Snæbjørn
источник
2

Если кто-то интересуется Arrayверсией, это выглядит так:

int i = Array.FindIndex(yourArray, x => x == itemYouWant);
Али Бордбар
источник