Ja to napisałem:
public static class EnumerableExtensions
{
public static int IndexOf<T>(this IEnumerable<T> obj, T value)
{
return obj
.Select((a, i) => (a.Equals(value)) ? i : -1)
.Max();
}
public static int IndexOf<T>(this IEnumerable<T> obj, T value
, IEqualityComparer<T> comparer)
{
return obj
.Select((a, i) => (comparer.Equals(a, value)) ? i : -1)
.Max();
}
}
Ale nie wiem, czy już istnieje, prawda?
Max
podejściem polega na tym, że a: szuka dalej, a b: zwraca ostatni indeks, gdy są duplikaty (ludzie zwykle oczekują pierwszego indeksu)ToList()/FindIndex()
wykonuje trik najlepiejOdpowiedzi:
Cały sens wyrzucania rzeczy jako IEnumerable polega na tym, abyś mógł leniwie iterować zawartość. W związku z tym tak naprawdę nie ma pojęcia indeksu. To, co robisz, naprawdę nie ma większego sensu dla IEnumerable. Jeśli potrzebujesz czegoś, co obsługuje dostęp przez indeks, umieść to na rzeczywistej liście lub kolekcji.
źródło
IEnumerable<>
. Nie kupuję całego dogmatu „powinieneś to robić”.IEnumerable
toEnumerable.ElementAt
by nie istniał.IndexOf
jest po prostu odwrotnością - każdy argument przeciwko temu musi mieć równe zastosowanieElementAt
.Zakwestionowałbym mądrość, ale może:
(używając
EqualityComparer<T>.Default
do emulacji w!=
razie potrzeby) - ale musisz oglądać, aby zwrócić -1, jeśli nie zostanie znaleziony ... więc może po prostu zrób to długoźródło
TakeWhile
jest sprytny! Pamiętaj jednak, że zwraca to,Count
jeśli element nie istnieje, co jest odchyleniem od standardowego zachowania.Wdrożyłbym to w ten sposób:
źródło
==
Może wymagać pracy?KVP<T, int?>
(nullable index)IList<T>
a jeśli tak, odrocz do jego metody IndexOf, na wypadek gdyby miała optymalizację specyficzną dla typu.Sposób, w jaki obecnie to robię, jest nieco krótszy niż te już sugerowane i, o ile wiem, daje pożądany efekt:
Jest trochę niezgrabny, ale spełnia swoje zadanie i jest dość zwięzły.
źródło
Najlepszym sposobem na złapanie pozycji jest
FindIndex
ta funkcja jest dostępna tylko dlaList<>
Przykład
Jeśli masz moduł wyliczający lub tablicę, użyj tego sposobu
lub
źródło
Myślę, że najlepszą opcją jest wdrożenie w ten sposób:
Nie utworzy również anonimowego obiektu
źródło
Wiem, że trochę późno w grze ... ale to właśnie ostatnio zrobiłem. Jest nieco inny niż twój, ale pozwala programiście dyktować, jaka ma być operacja równości (predykat). Co uważam za bardzo przydatne w przypadku różnych typów, ponieważ mam wtedy ogólny sposób robienia tego niezależnie od typu obiektu i
<T>
wbudowanego operatora równości.Ma również bardzo, bardzo małe zużycie pamięci i jest bardzo, bardzo szybki / wydajny ... jeśli o to ci chodzi.
Co gorsza, po prostu dodasz to do listy rozszerzeń.
W każdym razie ... oto jest.
Mam nadzieję, że to komuś pomoże.
źródło
GetEnumerator
iMoveNext
zamiast po prostuforeach
?foreach
wywołanieDispose
modułu wyliczającego, jeśli implementujeIDisposable
. (Zobacz stackoverflow.com/questions/4982396/ ) Ponieważ kod w tej odpowiedzi nie wie, czy wynik wywołaniaGetEnumerator
jest jednorazowy, czy nie, powinien zrobić to samo. W tym momencie wciąż nie jestem pewien, czy istnieje korzyść perfekcyjna, chociaż było trochę dodatkowego IL, którego cel nie rzucił się na mnie!foreach
pętlą, w którym to przypadku dla konkretnego przypadkuT
będąc typem wartości, może to oznaczać zapisanie operacji box / unbox przy użyciu pętli while. Jednak nie jest to potwierdzone przez IL, który otrzymałem z wersji Twojej odpowiedzi zforeach
. Nadal uważam jednak, że warunkowe usunięcie iteratora jest ważne. Czy możesz zmodyfikować odpowiedź, aby to uwzględnić?Kilka lat później, ale to używa Linq, zwraca -1, jeśli nie zostanie znaleziony, nie tworzy dodatkowych obiektów i powinno spowodować zwarcie, gdy zostanie znalezione [w przeciwieństwie do iteracji po całym IEnumerable]:
Gdzie „FirstOr” to:
źródło
source.Where
isource.DefaultIfEmpty
na przykład utworząIEnumerable
each.Natknąłem się na to dzisiaj w poszukiwaniu odpowiedzi i pomyślałem, że dodam swoją wersję do listy (gra słów nie zamierzona). Wykorzystuje zerowy operator warunkowy języka C # 6,0
Zrobiłem kilka „wyścigów starych koni” (testy) i dla dużych kolekcji (~ 100 000), najgorszy scenariusz, że przedmiot, który chcesz, jest na końcu, jest to 2x szybsze niż robienie
ToList().FindIndex()
. Jeśli żądany przedmiot znajduje się pośrodku, jest ~ 4x szybszy.W przypadku mniejszych kolekcji (~ 10 000) wydaje się, że jest tylko nieznacznie szybszy
Oto, jak to przetestowałem https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e
źródło
Korzystając z odpowiedzi @Marc Gravell, znalazłem sposób na użycie następującej metody:
aby otrzymać -1, gdy przedmiotu nie można znaleźć:
Myślę, że ten sposób może być zarówno najszybszy, jak i prostszy. Jednak nie testowałem jeszcze wydajności.
źródło
Alternatywą dla znalezienia indeksu po fakcie jest zawinięcie Enumerable, nieco podobne do użycia metody Linq GroupBy ().
Co daje przykład użycia:
źródło
Może to być naprawdę fajne z rozszerzeniem (działającym jako proxy), na przykład:
Który automagicznie przypisze indeksy do kolekcji dostępnej przez to
Index
właściwości.Berło:
Rozszerzenie niestandardowe (prawdopodobnie najbardziej przydatne do pracy z EF i DbContext):
źródło