Come ottenere l'indice di un elemento in un elenco in un solo passaggio?


193

Come posso trovare l'indice di un elemento in un elenco senza scorrere in esso?

Attualmente questo non sembra molto bello: cerca nell'elenco due volte lo stesso oggetto, solo per ottenere l'indice:

var oProp = something;

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

Risposte:


435

Che ne dite del metodo List.FindIndex :

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

Questo metodo esegue una ricerca lineare; pertanto, questo metodo è un'operazione O (n), dove n è Count.

Se l'elemento non viene trovato, verrà restituito -1


2
Che ne dici int index?
Dylan Czenski il

2
@DylanChensky ha codificato troppo JS
lennyy l'

9
Per riferimento, se l'articolo non viene trovato; restituirà -1
Daniel Filipe il

103

Per tipi semplici puoi usare "IndexOf":

List<string> arr = new List<string>();
arr.Add("aaa");
arr.Add("bbb");
arr.Add("ccc");
int i = arr.IndexOf("bbb"); // RETURNS 1.

71

EDIT: se stai usando solo un List<>e hai solo bisogno dell'indice, allora List.FindIndexè davvero l'approccio migliore. Lascerò questa risposta qui per coloro che hanno bisogno di qualcosa di diverso (ad esempio sopra a tutti IEnumerable<>).

Usa il sovraccarico di Selectcui accetta un indice nel predicato, quindi trasformi il tuo elenco in una coppia (indice, valore):

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

Poi:

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

O se desideri solo l'indice e lo stai utilizzando in più punti, puoi facilmente scrivere il tuo metodo di estensione che era simile Where, ma invece di restituire gli articoli originali, ha restituito gli indici di quegli elementi che corrispondevano al predicato.


Sembra che tutto ciò che vuole sia l'indice. Elenco <>. FindIndex (Predicate <>) è l'approccio migliore. Sebbene il titolo della domanda insinui altrimenti, la descrizione del PO è abbastanza chiara, ha solo bisogno dell'indice "int theThingIActuallyAmInterestedIn"
Louis Ricci,

1
@LastCoder: Aha - aveva perso FindIndex. Sì, sono completamente d'accordo.
Jon Skeet,

Giusto per essere chiari, "indice / valore -> singolo" approccio "è migliore" (qui significa essere più veloce in termini di Big-O) che iterare manualmente due volte? Oppure il provider LINQ2Objects è abbastanza intelligente da ottimizzare una delle iterazioni? (Sto assumendo che sia Select che Single in generale siano operazioni O (n))
sara

1
@kai: Penso che devi leggere su come funziona LINQ, in pratica. È troppo complicato spiegare in dettaglio in un commento. Tuttavia ... questo sta solo ripetendo la raccolta dei sorgenti una volta. LINQ imposta una pipeline, che trasforma pigramente la sequenza di input in un'altra sequenza, quindi l' Single()operazione scorre su quella sequenza e trova il singolo elemento che corrisponde al predicato. Per maggiori dettagli, leggi la mia serie di blog su edulinq
Jon Skeet

1
+1 Avevo bisogno di questa soluzione. Boss pensò che fossi intelligente per una volta. Mi è stato consigliato di documentarlo attentamente, dal momento che utilizzava un tipo anonimo e potrebbe non essere chiaro al programmatore successivo nell'area.
Adam Wells,

14

Se non vuoi usare LINQ, allora:

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

in questo modo stai iterando l'elenco solo una volta.


22
@KingKing nessuno ha detto che lo è.
Tomer W

1
È la stessa implementazione di Linq FindIndexfuori interesse?
Coop

2
probabilmente non è lo stesso codice, List ha alcune ottimizzazioni ordinate qua e là. ma trovo difficile credere che possano cercare un elenco non ordinato in meno di O (n), quindi direi che probabilmente sono molto simili nella pratica.
Sara

6
  1. Soluzione semplice per trovare l'indice per qualsiasi valore di stringa nell'elenco.

Ecco il codice per List Of String:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Soluzione semplice per trovare l'indice per qualsiasi valore intero nell'elenco.

Ecco il codice per l'elenco di numeri interi:

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

2

Ecco un metodo di estensione copia / incolla per 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;
    }
}

Godere.


2

Se qualcuno si chiede la Arrayversione, va così:

int i = Array.FindIndex(yourArray, x => x == itemYouWant);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.