Jak uzyskać indeks pozycji na liście w jednym kroku?

193

Jak znaleźć indeks elementu na liście bez przechodzenia przez niego?

Obecnie nie wygląda to zbyt ładnie - dwukrotne przeszukiwanie listy tego samego elementu, aby uzyskać indeks:

var oProp = something;

int theThingIActuallyAmInterestedIn = myList.IndexOf(myList.Single(i => i.Prop == oProp));
Daniel Robinson
źródło

Odpowiedzi:

435

Co powiesz na metodę List.FindIndex :

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

Ta metoda wykonuje wyszukiwanie liniowe; dlatego ta metoda jest operacją O (n), gdzie n to Liczba.

Jeśli element nie zostanie znaleziony, zwróci -1

Alex Filipovici
źródło
2
Jak o int index?
Dylan Czenski
2
@DylanChensky za dużo koduje JS
lennyy,
9
Dla odniesienia, jeśli element nie zostanie znaleziony; zwróci -1
Daniel Filipe
102

W przypadku prostych typów możesz użyć „IndexOf”:

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
źródło
71

EDIT: Jeśli tylko przy użyciu List<>i tylko trzeba indeks, wtedy List.FindIndexjest rzeczywiście najlepszym rozwiązaniem. Pozostawię tę odpowiedź tutaj tym, którzy potrzebują czegoś innego (np. Na dowolnym IEnumerable<>).

Użyj przeciążenia, Selectktóre pobiera indeks w predykacie, więc przekształcasz listę w parę (indeks, wartość):

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

Następnie:

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

Lub jeśli chcesz tylko indeksu i używasz go w wielu miejscach, możesz łatwo napisać własną metodę rozszerzenia, która była podobna Where, ale zamiast zwracać oryginalne elementy, zwróciło indeksy tych elementów, które pasowały do ​​predykatu.

Jon Skeet
źródło
Wygląda na to, że chce tylko indeksu. Lista <>. FindIndex (Predicate <>) jest najlepszym podejściem. Chociaż tytuł pytania sugerowałby inaczej, opis OP jest dość jasny, że potrzebuje on jedynie indeksu „int theThingIActuallyAmInterestedIn”
Louis Ricci
1
@LastCoder: Aha - brakowało FindIndex. Tak, całkowicie się zgadzam.
Jon Skeet
Dla jasności, czy „indeks / wartość -> pojedyncze” podejście jest „lepsze” (tutaj oznacza to, że jest szybszy pod względem Big-O) niż ręczne iterowanie dwa razy? A może dostawca LINQ2Objects jest wystarczająco inteligentny, aby zoptymalizować jedną z iteracji? (Zakładam, że ogólnie rzecz biorąc, zarówno Select, jak i Single są operacjami O (n))
sara
1
@kai: Myślę, że powinieneś przeczytać, jak działa LINQ. To zbyt skomplikowane, aby wyjaśnić szczegółowo w komentarzu. Jednak ... jest to tylko jeden raz iteracja kolekcji źródłowej. LINQ konfiguruje potok, który leniwie przekształca sekwencję wejściową w inną sekwencję, a następnie Single()operacja iteruje tę sekwencję i znajduje pojedynczy element, który pasuje do predykatu. Aby uzyskać więcej informacji, przeczytaj moją serię blogów edulinq
Jon Skeet
1
+1 Potrzebowałem tego rozwiązania. Szef pomyślał, że raz jestem mądry. Poradzono mi dokładnie to udokumentować, ponieważ używał anonimowego typu i może nie być jasne dla następnego kodera w okolicy.
Adam Wells,
14

Jeśli nie chcesz używać LINQ, to:

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

w ten sposób iterujesz listę tylko raz.

gzaxx
źródło
22
@KingKing nikt nie powiedział, że tak.
Tomer W
1
Czy to ta sama implementacja, co Linq, FindIndexnie interesuje?
Coops
2
prawdopodobnie nie ten sam kod, List ma jakieś fajne optymalizacje tu i tam. ale trudno mi uwierzyć, że mogą przeszukiwać nieuporządkowaną listę w mniej niż O (n), więc powiedziałbym, że w praktyce są bardzo podobne.
sara,
6
  1. Proste rozwiązanie, aby znaleźć indeks dla dowolnej wartości ciągu na liście.

Oto kod dla listy ciągów:

int indexOfValue = myList.FindIndex(a => a.Contains("insert value from list"));
  1. Proste rozwiązanie do znalezienia indeksu dla dowolnej liczby całkowitej na liście.

Oto kod dla listy liczb całkowitych:

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

źródło
2

Oto metoda rozszerzenia do kopiowania / wklejania dla 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;
    }
}

Cieszyć się.

Snæbjørn
źródło
2

Jeśli ktoś zastanawia się nad Arraywersją, wygląda to tak:

int i = Array.FindIndex(yourArray, x => x == itemYouWant);
Ali Bordbar
źródło