Comment prendre tous, mais le dernier élément dans une séquence à l'aide de LINQ?
Disons que j'ai une séquence.
IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
//sequence now contains: 0,1,2,3,...,999999,1000000
L'obtention de la séquence n'est pas bon marché et est généré dynamiquement, et je veux parcourir une fois seulement.
Je veux obtenir 0 - 999999 (c'est à dire tout sauf le dernier élément)
Je reconnais que je pourrais faire quelque chose comme:
sequence.Take(sequence.Count() - 1);
mais que les résultats dans les deux énumérations sur le gros de la séquence.
Est-il LINQ construction qui me permet de le faire:
sequence.TakeAllButTheLastElement();
- Vous avez le choix entre une O(2n) en temps O(comte) de l'espace de l'efficacité de l'algorithme, lorsque celui-ci a également besoin de déplacer des éléments dans un tableau interne.
- Dario, pourriez-vous nous expliquer pour quelqu'un qui n'est pas que dans les grandes o-notation?
- Voir également cette double question: stackoverflow.com/q/4166493/240733
- J'ai fini avec la mise en cache par la conversion de la collecte à la Liste, puis en appelant
sequenceList.RemoveAt(sequence.Count - 1);
. Dans mon cas, il est acceptable parce que, après tout LINQ manipulations je dois le convertir en tableau ouIReadOnlyCollection
de toute façon. Je me demande ce qui est votre cas où vous ne l'envisage même pas de mise en cache? Comme je peux le voir, même approuvé réponse fait un peu de mise en cache si simple de conversion deList
est beaucoup plus facile et plus rapide à mon avis.
Vous devez vous connecter pour publier un commentaire.
Je ne sais pas Linq solution - Mais vous pouvez facilement le code de l'algorithme par vous-même à l'aide de générateurs (taux de retour).
Ou comme une solution généralisée de jeter les n derniers éléments (en utilisant une file d'attente comme suggéré dans les commentaires):
Comme une alternative à la création de votre propre méthode et dans un cas, les éléments d'ordre n'est pas important, la prochaine sera d'oeuvre:
equence.Reverse().Skip(1).Reverse()
pas une bonne solutionParce que je ne suis pas un fan de manière explicite à l'aide d'un
Enumerator
, voici une alternative. Notez que le wrapper méthodes sont nécessaires pour laisser arguments invalides jeter début, plutôt que de s'en remettre les chèques jusqu'à ce que la séquence est dénombrés.Comme par Eric Lippert la suggestion, il se généralise à n éléments:
Où j'ai maintenant tampon avant rendement au lieu de après rendement, de sorte que le
n == 0
cas n'a pas besoin d'un traitement spécial.buffered=false
dans une clause else avant d'assignerbuffer
. La condition est déjà vérifié tout de même, mais cela permettrait d'éviter de manière redondante réglagebuffered
chaque passage dans la boucle.DropLast
. Sinon, la validation se produit uniquement lorsque vous énumérer la séquence (sur le premier appel àMoveNext
sur laIEnumerator
). Pas une chose amusante à déboguer quand il pourrait y avoir une quantité arbitraire de code entre la génération de laIEnumerable
et en fait l'énumération d'elle. Aujourd'hui, j'écriraisInternalDropLast
comme une fonction interne deDropLast
, mais cette fonctionnalité n'existe pas en C# quand j'ai écrit ce code il y a 9 ans.Pour ceux qui utilisent une version plus récente de .net, le
Enumerable.SkipLast(IEnumerable<TSource>, Int32)
méthode a été ajouté dans .NET Core 2.0Source: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast
Rien dans la BCL (ou MoreLinq je crois), mais vous pouvez créer votre propre méthode d'extension.
if (!first)
et tirezfirst = false
de la si.prev
dans la première itération, et boucle pour toujours après que".using
déclaration maintenant.Il serait utile si .NET Framework a été livré avec une extension de la méthode de ce genre.
si vous n'avez pas le temps pour le déploiement de votre propre poste, voici un moyen plus rapide:
Une légère expansion sur Joren la solution élégante:
Où rétrécir implémente un simple comptage de l'avant de supprimer la première
left
de nombreux éléments et la même jeté tampon de laisser tomber la dernièreright
de nombreux éléments.Si vous pouvez obtenir le
Count
ouLength
d'une énumération, qui, dans la plupart des cas, vous peut, alors il suffitTake(n - 1)
Exemple avec des tableaux
Exemple avec
IEnumerable<T>
Une légère variation sur la accepté de répondre, ce qui (à mon goût) est un peu plus simple:
Pourquoi ne pas simplement
.ToList<type>()
sur la séquence, puis nombre d'appels et de prendre comme vous l'avez fait à l'origine..mais depuis il a été tiré dans une liste, il ne faut pas faire, cher énumération deux fois. Droit?La solution que j'utilise pour ce problème est un peu plus élaboré.
Mon util statique classe contient une méthode d'extension
MarkEnd
qui convertit lesT
-éléments dansEndMarkedItem<T>
-éléments. Chaque élément est marqué avec un supplément deint
, qui est soit 0; ou (dans le cas où on s'intéresse plus particulièrement au cours des 3 éléments) -3, -2, ou -1 pour les 3 derniers articles.Cela pourrait être utile sur son propre, par exemple lorsque vous souhaitez créer une liste dans un simple
foreach
en boucle avec des virgules après chaque élément, sauf les 2 derniers, avec l'avant-dernier élément, puis par une conjonction de mots (comme “et” ou “ou”), et le dernier élément suivi d'un point.Pour la génération de l'ensemble de la liste sans la dernière n éléments, la méthode d'extension
ButLast
simplement parcourt laEndMarkedItem<T>
s, tandis queEndMark == 0
.Si vous ne spécifiez pas
tailLength
, seul le dernier élément est marqué (enMarkEnd()
) ou abandonnées (enButLast()
).Comme les autres solutions, cela fonctionne par mise en mémoire tampon.
Je ne pense pas qu'il peut obtenir plus succincte que cette - aussi de s'assurer de Disposer de
IEnumerator<T>
:Edit: techniquement identique à cette réponse.
Vous pourriez écrire:
C'est un général et à mon humble avis solution élégante qui va gérer tous les cas correctement:
Mon traditionnel
IEnumerable
approche:Un moyen simple serait de simplement convertir à une file d'attente et la file d'attente jusqu'à ce que le nombre d'articles que vous voulez sauter à gauche.
Pourrait être:
J'imagine que ça doit être comme de "Où", mais la préservation de l'ordre(?).
Si la vitesse est une exigence, cette ancienne école doit être le plus rapide, même si le code n'a pas l'air aussi lisse que linq pourraient le faire.
Cela exige que la séquence est un tableau, car il a une longueur fixe et d'éléments indexés.
Je serais probablement faire quelque chose comme ceci:
C'est une itération avec un chèque qu'il n'est pas le dernier pour chaque temps si.