Wie erhalte ich den Index eines Elements in einer Liste in einem einzigen Schritt?

193

Wie kann ich den Index eines Elements in einer Liste finden, ohne ihn zu durchlaufen?

Derzeit sieht das nicht besonders gut aus. Durchsuchen Sie die Liste zweimal nach demselben Element, um den Index zu erhalten:

var oProp = something;

int theThingIActuallyAmInterestedIn = myList.IndexOf(myList.Single(i => i.Prop == oProp));
Daniel Robinson
quelle

Antworten:

434

Wie wäre es mit der List.FindIndex-Methode :

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

Diese Methode führt eine lineare Suche durch. Daher ist diese Methode eine O (n) -Operation, wobei n Count ist.

Wenn das Element nicht gefunden wird, wird -1 zurückgegeben

Alex Filipovici
quelle
2
Wie wäre es int index?
Dylan Czenski
2
@ DylanChensky er hat JS zu viel
codiert
9
Als Referenz, wenn der Artikel nicht gefunden wird; es wird -1
Daniel Filipe
102

Für einfache Typen können Sie "IndexOf" verwenden:

List<string> arr = new List<string>();
arr.Add("aaa");
arr.Add("bbb");
arr.Add("ccc");
int i = arr.IndexOf("bbb"); // RETURNS 1.
Jose Manuel Abarca Rodríguez
quelle
71

BEARBEITEN: Wenn Sie nur a verwenden List<>und nur den Index benötigen, List.FindIndexist dies in der Tat der beste Ansatz. Ich werde diese Antwort hier für diejenigen hinterlassen, die etwas anderes brauchen (z. B. obendrein IEnumerable<>).

Verwenden Sie die Überladung, für Selectdie ein Index im Prädikat verwendet wird, damit Sie Ihre Liste in ein (Index-, Wert-) Paar umwandeln:

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

Dann:

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

Oder wenn Sie nur den Index möchten und diesen an mehreren Stellen verwenden, können Sie leicht eine eigene Erweiterungsmethode schreiben Where, die der folgenden entspricht. Statt jedoch die ursprünglichen Elemente zurückzugeben, werden die Indizes der Elemente zurückgegeben, die dem Prädikat entsprechen.

Jon Skeet
quelle
Anscheinend will er nur den Index. List <>. FindIndex (Predicate <>) ist der beste Ansatz. Obwohl der Fragentitel anders andeuten würde, ist die Beschreibung des OP ziemlich klar, dass er nur den Index "int theThingIActuallyAmInterestedIn" benötigt
Louis Ricci
1
@ LastCoder: Aha - hatte FindIndex verpasst. Ja, ich stimme vollkommen zu.
Jon Skeet
Ist der "Index / Wert -> Einzel" -Ansatz "besser" (hier bedeutet dies, dass er in Bezug auf Big-O schneller ist) als zweimal manuell zu iterieren? Oder ist der LINQ2Objects-Anbieter intelligent genug, um eine der Iterationen zu optimieren? (Ich gehe davon aus, dass sowohl Select als auch Single im Allgemeinen O (n) -Operationen sind)
Sara
1
@kai: Ich denke, Sie müssen sich im Grunde darüber informieren, wie LINQ funktioniert. Es ist zu kompliziert, dies in einem Kommentar ausführlich zu erklären. Dies wird jedoch nur einmal über die Quellensammlung wiederholt. LINQ richtet eine Pipeline ein, die die Eingabesequenz träge in eine andere Sequenz umwandelt. Anschließend Single()durchläuft die Operation diese Sequenz und findet das einzelne Element, das dem Prädikat entspricht. Weitere Informationen finden Sie in meiner Edulinq-Blogserie: codeblog.jonskeet.uk/category/edulinq
Jon Skeet
1
+1 Ich brauchte diese Lösung. Boss dachte, ich wäre einmal schlau. Es wurde mir empfohlen, dies sorgfältig zu dokumentieren, da es sich um einen anonymen Typ handelt und für den nächsten Codierer in der Umgebung möglicherweise nicht klar ist.
Adam Wells
14

Wenn Sie LINQ nicht verwenden möchten, gehen Sie wie folgt vor:

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

Auf diese Weise iterieren Sie die Liste nur einmal.

gzaxx
quelle
22
@KingKing niemand sagte es ist.
Tomer W
1
Ist dies die gleiche Implementierung wie Linq FindIndexaus Interesse?
Coops
2
wahrscheinlich nicht der gleiche Code, List hat hier und da einige nette Optimierungen. aber es fällt mir schwer zu glauben, dass sie eine ungeordnete Liste in weniger als O (n) durchsuchen können, daher würde ich sagen, dass sie sich in der Praxis wahrscheinlich sehr ähnlich sind.
Sara
6
  1. Einfache Lösung, um einen Index für einen beliebigen Zeichenfolgenwert in der Liste zu finden.

Hier ist der Code für die Liste der Zeichenfolgen:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Einfache Lösung, um einen Index für einen beliebigen Integer-Wert in der Liste zu finden.

Hier ist der Code für die Liste der Ganzzahlen:

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

quelle
2

Hier ist eine kopier- / einfügefähige Erweiterungsmethode für 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;
    }
}

Genießen.

Snæbjørn
quelle
2

Wenn sich jemand für die ArrayVersion wundert , geht es so:

int i = Array.FindIndex(yourArray, x => x == itemYouWant);
Ali Bordbar
quelle