Obtenir précédent et suivant dans un IEnumerable à l'aide de LINQ
J'ai un IEnumerable d'un type personnalisé. (Que j'ai obtenu à partir d'un SelectMany)
J'ai aussi un élément (myItem) dans ce IEnumerable que je désir, le précédent et le point suivant à partir de l'interface IEnumerable.
Actuellement, je suis en train de faire le désiré comme ceci:
var previousItem = myIEnumerable.Reverse().SkipWhile(
i => i.UniqueObjectID != myItem.UniqueObjectID).Skip(1).FirstOrDefault();
Je peux obtenir l'élément suivant simplement ommitting la .Reverse
.
ou, j'ai pu:
int index = myIEnumerable.ToList().FindIndex(
i => i.UniqueObjectID == myItem.UniqueObjectID)
et ensuite utiliser .ElementAt(index +/- 1)
pour obtenir le précédent ou suivant.
- Ce qui est mieux entre les deux options?
- Est-il une option encore meilleure disponible?
"Mieux" comprend une combinaison de la performance (mémoire et de la vitesse) et la lisibilité; à la lisibilité étant ma principale préoccupation.
- Compte tenu de la lisibilité, de Votre première option est génial! Beaucoup mieux que dans les réponses. Va l'utiliser! Merci!
- Je suis d'accord avec @Aleksei, à l'aide de celui-là aussi
Vous devez vous connecter pour publier un commentaire.
Tout d'abord
En général, vous ne pouvez pas avoir les deux, la règle de base est que, si vous optimiser pour la vitesse, il vous en coûtera la mémoire, si vous optimiser pour mémoire, il vous en coûtera de vitesse.
Il existe une meilleure solution, qui fonctionne bien sur à la fois la mémoire et la vitesse des fronts, et peut être utilisé d'une manière lisible (je ne suis pas heureux avec le nom de la fonction, cependant,
FindItemReturningPreviousItemFoundItemAndNextItem
est un peu de la taille d'une bouchée).Donc, on dirait qu'il est temps pour une mesure de trouver la méthode d'extension, quelque chose comme . . .
Vous pouvez mettre en cache le résultat de cette liste si vous avez besoin de se référer à des articles plus d'une fois, mais il renvoie l'élément trouvé, précédée de la rubrique précédente, suivie par l'élément suivant. par exemple,
Les valeurs par défaut de revenir si c'est le premier ou le dernier élément peut être nécessaire de changer en fonction de vos besoins.
Espère que cette aide.
List<>
juste pour rendre la recherche plus facile (parfois c'est pas pratique), et même si vous n'êtes jamais pour ce faire, une fois que vous aurez besoin de coder quelque chose de similaire. À mon humble avis en le mettant dans sa propre fonction n'est pas sorcier, le rendant générique est aussi facile que de ne pas. Le seul vrai problème que j'ai avec le code est le nom de la fonction..ToList()
pour obtenir les éléments; oh bien. J'ai aussi renommé la fonction deFindSurroundingItems<T>
qui est assez précis pour mes fins.default(T)
éléments. Je serais heureux de upvote cette réponse une fois la correction proposée est mise en œuvre (astuce: vous devez remplacer labreak;
avecyield break;
).FindTripple()
.FindExcentraGallumbits()
Pour des raisons de lisibilité, j'avais la charge de la
IEnumerable
dans une liste chaînée:Queue
à partir d'unIEnumerable
sera copier les références dans leIEnumerable
. Afin de ne pas copier les objets pointés par laIEnumerable
, mais il copie les références à ces objets. C'est aussi cher qu'il y a d'éléments dans la séquence. Si c'était quelque chose que j'avais besoin de le faire régulièrement en boucle, je l'avais trouvé d'une autre manière. qui a dit que je n'ai rien contre les Listes chaînées et des Files d'attente, mais je ne l'aversion inutile d'effectuer des copiesPar la création d'une extension de la méthode pour l'établissement d'un contexte à l'élément en cours vous pouvez utiliser une requête Linq comme ceci:
L'extension serait quelque chose comme ceci:
Concat
au lieuUnion
.Vous pourriez mettre en cache les énumérables dans une liste
itérer sur elle par l'indice de
alors l'élément courant est
myList[i]
, l'élément précédent estmyList[i-1]
, et l'élément suivant estmyList[i+1]
(Ne pas oublier le cas particulier de la première et de la dernière éléments dans la liste.)
%
sur le[i-1]
[i+1]
entrées.ToArray()
est légèrement plus rapide que.ToList()
, et immédiatement démontrer l'impossibilité de modifier, ce qui est approprié ici - pour readablility.CPU
Dépend entièrement de l'endroit où l'objet est dans la séquence. Si elle est située à la fin j'attendrais la deuxième à être plus rapide avec plus d'un facteur 2 (mais seulement un facteur constant). Si elle est située au début de la première sera plus rapide parce que vous n'avez pas à parcourir toute la liste.
Mémoire
La première est une itération de la séquence sans enregistrer la séquence de sorte que la mémoire de hit sera très petite. La deuxième solution sera de prendre autant de mémoire que la longueur de la liste * références + objets + les frais généraux.
Vous êtes vraiment sur de compliquer les choses:
Parfois juste un
for
boucle va être de mieux à faire quelque chose, et je pense que fournir une meilleure mise en œuvre de ce que vous essayez de faire/J'ai pensé que je voudrais essayer de répondre à cette aide Zip de Linq.
Cela renvoie un type anonyme {précédente,le courant,la prochaine}.
Malheureusement, cela ne fonctionne que pour les indices 1, 2 et 3.
Cette version fonctionnera pour tous les index.
À la fois utiliser la version d'évaluation différée de courtoisie de Linq.
Si vous en avez besoin pour chaque élément dans myIEnumerable je venais de parcourir à garder les références aux 2 éléments précédents. Dans le corps de la boucle que j'ai fais le traitement pour le deuxième élément précédent et l'actuel serait son héritier et le premier de son ancêtre.
Si vous en avez besoin pour un seul élément que j'avais à choisir votre première approche.