Comment faire pour obtenir l'index d'un élément dans un IEnumerable?
J'ai écrit ceci:
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();
}
}
Mais je ne sais pas si elle existe déjà, n'est ce pas?
- Le problème avec un
Max
approche est que: il ne cesse de regarder, et b: elle retourne la dernière index quand il y a des doublons (les gens s'attendent généralement à ce que le premier indice) - geekswithblogs.net compare 4 solutions et de leur performance. Le
ToList()/FindIndex()
astuce fonctionne mieux
Vous devez vous connecter pour publier un commentaire.
Le point de faire les choses comme IEnumerable est de sorte que vous pouvez paresseusement parcourir le contenu. En tant que tel, il n'y a pas vraiment un concept d'un index. Ce que vous faites vraiment ne fait pas beaucoup de sens pour un IEnumerable. Si vous besoin de quelque chose qui prend en charge l'accès par index, le mettre dans une liste réelle ou de la collection.
IEnumerable<>
. Je n'achète pas tout "vous shoul ferais ce" dogme.IEnumerable
puisEnumerable.ElementAt
n'existerait pas.IndexOf
est tout simplement l'inverse -- aucun argument contre elle doit également s'appliquer auxElementAt
.SortedSet<T>
, qui veille au respect de l'unicité de chacun de ses membres?IndexOf
pour être significative, l'interface IEnumerable doivent être commandés (à ne pas confondre avec triés), et doit pouvoir être dénombrées plus d'une fois. Je soupçonne que cette méthode n'est pas une partie de LINQ parce que pas tous les IEnumerables répondent à ces critères. Mais, dans presque tous les cas où vous souhaitez utiliser ce que vous savez que la collection sous-jacente NE répondent pas à ces critères, alors je souhaite qu'il ont été inclus.IEnumerable
être assez souple, ils ne doivent pas se limiter à reproductible collections, mais doit permettre un passage seulement paradigmes. DoncIndexOf
peut-être trop restrictive. Peut-être une meilleure place serait dansICollection<T>
qui est plus comme un tableau ou une liste type de collecte.J'avais en question la sagesse, mais peut-être:
(à l'aide de
EqualityComparer<T>.Default
pour émuler!=
si nécessaire) - mais vous avez besoin de regarder pour retourner -1 si pas trouvé... donc peut-être juste faire le long cheminTakeWhile
est intelligent! Gardez à l'esprit ceci renvoieCount
si l'élément n'existe pas, qui est une déviation de la norme de comportement.Je voudrais mettre en œuvre comme ceci:
==
pourriez avoir besoin de travailler?KVP<T, int?>
(nullable index)IList<T>
et, dans l'affirmative, de reporter à sa méthode IndexOf juste au cas où il a un type spécifique d'optimisation.La façon dont je suis en train de faire, c'est un peu plus courts que ceux déjà suggéré, et aussi loin que je peux dire donne le résultat souhaité:
C'est un peu maladroit, mais il fait le travail et est assez concis.
Je pense que la meilleure option est de mettre en place comme ceci:
Il ne sera également pas créer l'objet anonyme
Le meilleur moyen de détecter la position est par
FindIndex
Cette fonction est disponible uniquement pour lesList<>
Exemple
Si vous avez énumérateur de tableau ou de l'utiliser de cette façon
ou
Un peu en retard dans le jeu, je sais... mais c'est ce que j'ai fait récemment. Elle est légèrement différente de la vôtre, mais permet au programmeur de dicter ce que l'opération d'égalité doit être (prédicat). Ce que je trouve très utiles lorsqu'il s'agit de différents types, depuis que je puis avoir un générique façon de le faire quel que soit le type d'objet et
<T>
construit en opérateur d'égalité.Il a également une très très petite empreinte mémoire, et est très, très rapide/efficace... si vous vous souciez de cela.
Au pire, il vous suffit de l'ajouter à votre liste d'extensions.
De toute façon... c'est ici.
J'espère que cela aide quelqu'un.
GetEnumerator
etMoveNext
plutôt que juste unforeach
?foreach
appelleraDispose
sur l'énumérateur si elle met en œuvreIDisposable
. (Voir stackoverflow.com/questions/4982396/...) Comme le code de cette réponse ne sais pas si le résultat de l'appelGetEnumerator
est ou n'est pas jetable, il doit faire de même. À ce point, je ne suis toujours pas clair qu'il y a une perf d'avantages, mais il y avait une certaine supplémentaire de IL dont le but n'est pas de jaillir hors de moi!foreach
boucle, dans ce cas, pour le cas particulier deT
être un type de la valeur qu'il pourrait être l'enregistrement d'un box/unbox opération à l'aide de la boucle while. Cependant, ce n'est pas corroborée par le IL que j'ai obtenu à partir d'une version de votre réponse avecforeach
. Je ne pense toujours que la condition de l'élimination de l'itérateur est important, cependant. Pourriez-vous modifier la réponse de l'inclure?Quelques années plus tard, mais il utilise Linq, retourne -1 si pas trouvé, n'est pas de créer des objets en plus, et devrait de court-circuit, une fois trouvé, [par opposition à une itération sur l'ensemble de la IEnumerable]:
Où "FirstOr' est:
source.Where
etsource.DefaultIfEmpty
, par exemple, créer unIEnumerable
chaque.Une alternative pour trouver l'indice, après les faits, est d'envelopper le Énumérable, un peu similaire à l'aide de Linq GroupBy() la méthode.
Qui donne un cas d'utilisation d':
Suis tombé sur ça aujourd'hui, en quête de réponses et je pensais que je voudrais ajouter ma version de la liste (Aucun calembour prévu). Il utlises le null opérateur conditionnel de c#6.0
J'ai fait de la course des vieux chevaux (test) et pour les grandes collections (~100 000 habitants), le scénario du pire cas que le point que vous voulez est à la fin, c'est 2x plus rapide que de faire
ToList().FindIndex()
. Si l'Élément que vous voulez, c'est au milieu de ses ~4x plus rapide.Pour les petites collections (~10 000 habitants), il semble à peine plus rapide
Heres comment je l'ai testé https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e
À l'aide de @Marc Gravel 's réponse, j'ai trouvé un moyen d'utiliser la méthode suivante:
afin d'obtenir -1 si l'élément ne peut pas être trouvé:
Je suppose que cela pourrait être à la fois le plus rapide et le plus simple. Cependant, je n'ai pas testé les performances encore.
Cela peut être vraiment cool avec une extension (comme le fonctionnement d'un proxy), par exemple:
Qui sera automatiquement attribuer des indices de la collection accessible via ce
Index
propriété.Interface:
Extension personnalisée (sans doute plus utile pour le travail avec les objectifs EF et DbContext):