Question

Je avais besoin d'une méthode pour me donner tout sauf le dernier élément d'une séquence. Ceci est mon implémentation actuelle:

    public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
    {
        using (IEnumerator<T> iterator = source.GetEnumerator())
        {
            if(iterator.MoveNext())
                while(true)
                {
                    var current = iterator.Current;
                    if(!iterator.MoveNext())
                        yield break;
                    yield return current;
                }
        }
    }

Ce que je besoin pour est de faire quelque chose avec tous les éléments sauf le dernier. Dans mon cas, j'ai une séquence d'objets avec différentes propriétés. Je les commande puis par date, et je dois faire un ajustement à tous sauf le dernier élément (ce qui serait le dernier après la commande).

La chose est, je ne suis pas encore trop dans ces agents recenseurs et des trucs et ne pas vraiment personne ici pour demander soit: p Ce que je me demande est si cela est une bonne mise en œuvre, ou si je l'ai fait une petite ou grande bévue quelque part. Ou si peut-être cette prise sur le problème est bizarre, etc.

Je suppose une application plus générale aurait pu être une méthode AllExceptMaxBy. Depuis ce genre est de ce qu'il est. MoreLinq a une méthode MaxBy et MinBy et ma méthode sorte de besoin de faire le même, mais retourner tous les éléments sauf celui maximum ou minimum.

Était-ce utile?

La solution

Ceci est délicat, comme « dernier élément » n'est pas un point d'arrêt de Markov: vous ne pouvez pas dire que vous avez le dernier élément jusqu'à ce que vous essayez d'obtenir le prochain. Il est faisable, mais seulement si cela ne vous dérange pas en permanence être « un élément derrière ». C'est essentiellement ce que votre implémentation actuelle fait, et il semble bien, bien que je serais probablement écrire un peu différemment.

Une autre approche serait d'utiliser foreach, ce qui donne toujours la valeur retournée précédemment, sauf si vous étiez à la première itération:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
    T previous = default(T);
    bool first = true;
    foreach (T element in source)
    {
        if (!first)
        {
            yield return previous;
        }
        previous = element;
        first = false;
    }
}

Une autre option, plus proche de votre code:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        if(!iterator.MoveNext())
        {
            yield break;
        }
        T previous = iterator.Current;
        while (iterator.MoveNext())
        {
            yield return previous;
            previous = iterator.Current;
        }
    }
}

On évite ainsi l'imbrication tout aussi profondément (en faisant une sortie précoce si la séquence est vide) et il utilise un « vrai » en état au lieu de while(true)

Autres conseils

Votre mise en œuvre semble parfaitement bien pour moi -. C'est probablement la façon dont je le ferais

La seule simplification que je pourrais suggérer par rapport à votre situation est de commander la liste l'inverse (à savoir par ordre croissant plutôt que décroissant). Bien que cela puisse ne pas être approprié dans votre code, il vous permet d'utiliser simplement collection.Skip(1) prendre tous les articles sauf le plus récent.

Si cela est impossible, pour des raisons que vous avez ne figurent pas dans votre message, votre implémentation actuelle est sans aucun problème.

Si vous utilisez .NET 3.5, je suppose que vous pouvez utiliser:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
  return source.TakeWhile((item, index) => index < source.Count() - 1))
}
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
    if (!source.Any())
    {
        yield break;
    }
    Queue<T> items = new Queue<T>();
    items.Enqueue(source.First());
    foreach(T item in source.Skip(1))
    {
        yield return items.Dequeue();
        items.Enqueue(item);
    }
}

(ancienne réponse mis au rebut;. Ce code a été testé et fonctionne) Il imprime
premier
deuxième
PREMIER
DEUXIÈME
TROISIÈME


public static class ExtNum{
  public static IEnumerable skipLast(this IEnumerable source){
    if ( ! source.Any())
      yield break;
    for (int i = 0 ; i <=source.Count()-2 ; i++ )
      yield return source.ElementAt(i);
    yield break;
  }
}
class Program
{
  static void Main( string[] args )
  {
    Queue qq = new Queue();
    qq.Enqueue("first");qq.Enqueue("second");qq.Enqueue("third");
    List lq = new List();
    lq.Add("FIRST"); lq.Add("SECOND"); lq.Add("THIRD"); lq.Add("FOURTH");
    foreach(string s1 in qq.skipLast())
      Console.WriteLine(s1);
    foreach ( string s2 in lq.skipLast())
      Console.WriteLine(s2);
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top