Disons que j'ai une séquence.
IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000
Obtenir la séquence n'est pas bon marché et est généré dynamiquement, et je veux l'itérer une seule fois.
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 cela se traduit par deux énumérations sur la grande séquence.
Existe-t-il une construction LINQ qui me permet de faire:
sequence.TakeAllButTheLastElement();
sequenceList.RemoveAt(sequence.Count - 1);
. Dans mon cas, c'est acceptable car après toutes les manipulations LINQ, je dois le convertir en tableau ou deIReadOnlyCollection
toute façon. Je me demande quel est votre cas d'utilisation où vous n'envisagez même pas la mise en cache? Comme je peux le voir, même la réponse approuvée fait une certaine mise en cache, si simple la conversion enList
est beaucoup plus facile et plus courte à mon avis.Réponses:
Je ne connais pas de solution Linq - Mais vous pouvez facilement coder l'algorithme vous-même à l'aide de générateurs (rendement de rendement).
Ou comme solution généralisée en supprimant les n derniers éléments (en utilisant une file d'attente comme suggéré dans les commentaires):
la source
Au lieu de créer votre propre méthode et dans un cas où l'ordre des éléments n'est pas important, la suivante fonctionnera:
la source
equence.Reverse().Skip(1).Reverse()
pas une bonne solutionParce que je ne suis pas fan de l'utilisation explicite d'un
Enumerator
, voici une alternative. Notez que les méthodes wrapper sont nécessaires pour laisser les arguments non valides être lancés tôt, plutôt que de différer les vérifications jusqu'à ce que la séquence soit réellement énumérée.Selon la suggestion d'Eric Lippert, il se généralise facilement à n éléments:
Où je tamponne maintenant avant de céder au lieu après avoir cédé, de sorte que le
n == 0
cas ne nécessite pas de traitement spécial.la source
buffered=false
une clause else avant de l'assignerbuffer
. La condition est déjà vérifiée de toute façon, mais cela éviterait un réglage redondant àbuffered
chaque fois dans la boucle.DropLast
. Sinon, la validation se produit uniquement lorsque vous énumérez réellement la séquence (lors du premier appel àMoveNext
sur le résultatIEnumerator
). Ce n'est pas une chose amusante à déboguer quand il pourrait y avoir une quantité arbitraire de code entre la générationIEnumerable
et l'énumération réelle. Aujourd'hui, j'écrirais enInternalDropLast
tant que fonction interne deDropLast
, mais cette fonctionnalité n'existait pas en C # lorsque j'ai écrit ce code il y a 9 ans.La
Enumerable.SkipLast(IEnumerable<TSource>, Int32)
méthode a été ajoutée dans .NET Standard 2.1. Il fait exactement ce que vous voulez.Depuis https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast
la source
Rien dans le BCL (ou MoreLinq je crois), mais vous pouvez créer votre propre méthode d'extension.
la source
if (!first)
et sortezfirst = false
du if.prev
dans la première itération et boucler pour toujours après cela».using
déclaration maintenant.Il serait utile que .NET Framework soit livré avec une méthode d'extension comme celle-ci.
la source
Une légère extension de la solution élégante de Joren:
Où shrink implémente un simple comptage en avant pour supprimer les premiers
left
éléments et le même tampon rejeté pour supprimer les derniersright
éléments.la source
si vous n'avez pas le temps de déployer votre propre extension, voici un moyen plus rapide:
la source
Une légère variation sur la réponse acceptée, qui (à mon goût) est un peu plus simple:
la source
Si vous pouvez obtenir le
Count
ouLength
d'un énumérable, ce que vous pouvez dans la plupart des cas, alorsTake(n - 1)
Exemple avec des tableaux
Exemple avec
IEnumerable<T>
la source
Pourquoi pas seulement
.ToList<type>()
sur la séquence, puis appelez count et prenez comme vous l'avez fait à l'origine ... mais comme il a été inséré dans une liste, il ne devrait pas faire une énumération coûteuse deux fois. Droite?la source
La solution que j'utilise pour ce problème est légèrement plus élaborée.
Ma classe util static contient une méthode d'extension
MarkEnd
qui convertit lesT
-items enEndMarkedItem<T>
-items. Chaque élément est marqué d'un extraint
, qui est soit 0 ; ou (au cas où l'on serait particulièrement intéressé par les 3 derniers éléments) -3 , -2 ou -1 pour les 3 derniers éléments.Cela peut être utile en soi, par exemple lorsque vous souhaitez créer une liste dans une simple
foreach
boucle avec des virgules après chaque élément sauf les 2 derniers, avec l'avant-dernier élément suivi d'un mot de conjonction (tel que " et " ou " ou »), et le dernier élément suivi d'un point.Pour générer la liste entière sans les n derniers éléments, la méthode d'extension
ButLast
itère simplement sur leEndMarkedItem<T>
s whileEndMark == 0
.Si vous ne spécifiez pas
tailLength
, seul le dernier élément est marqué (dansMarkEnd()
) ou supprimé (dansButLast()
).Comme les autres solutions, cela fonctionne par mise en mémoire tampon.
la source
la source
Je ne pense pas que cela puisse être plus succinct que cela - en veillant également à éliminer
IEnumerator<T>
:Edit: techniquement identique à cette réponse .
la source
Avec C # 8.0, vous pouvez utiliser des plages et des index pour cela.
Par défaut, C # 8.0 requiert .NET Core 3.0 ou .NET Standard 2.1 (ou supérieur). Cochez ce fil à utiliser avec des implémentations plus anciennes.
la source
Vous pourriez écrire:
la source
Il s'agit d'une solution élégante générale et à mon humble avis qui gérera correctement tous les cas:
la source
Mon
IEnumerable
approche traditionnelle :la source
Un moyen simple serait de simplement convertir en file d'attente et de retirer la file d'attente jusqu'à ce qu'il ne reste que le nombre d'éléments que vous souhaitez ignorer.
la source
Pourrait être:
Je suppose que ça devrait être comme de "Où" mais en préservant l'ordre (?).
la source
Si la vitesse est une exigence, cette méthode à l'ancienne devrait être la plus rapide, même si le code ne semble pas aussi fluide que linq pourrait le faire.
Cela nécessite que la séquence soit un tableau car elle a une longueur fixe et des éléments indexés.
la source
Je ferais probablement quelque chose comme ça:
Ceci est une itération avec une vérification que ce n'est pas la dernière à chaque fois.
la source