2010-09-23 2 views

Répondre

12
finiteList.Reverse().Take(count).Reverse(); 

ou

finiteList.Skip(finiteList.Count() - count) 

Il y a certains frais généraux, ce faisant, si une méthode personnalisée serait mieux.

Mise à jour: Une méthode personnalisée

public static class EnumerableExtensions 
{ 
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count) 
    { 
     if (source == null) throw new ArgumentNullException("source"); 
     if (count < 0) throw new ArgumentOutOfRangeException("count"); 

     if (count == 0) yield break; 

     var queue = new Queue<T>(count); 

     foreach (var t in source) 
     { 
      if (queue.Count == count) queue.Dequeue(); 

      queue.Enqueue(t); 
     } 

     foreach (var t in queue) 
      yield return t; 
    } 
} 

Mise à jour: modifié le code d'un littlebit avec des idées de dtb's répondre :-)

Commentaire Bear: Regardez ce exemple:

var lastFive = Enumerable.Range(1, 10).TakeLast(5); 
var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way 

Queue<int> q = (Queue<int>)lastFive2; 
q.Dequeue(); 

//Is lastFive2 still last five? no... 

Vous pourriez pote changer les valeurs de lastFive2 et donc cette approche peut être dangereuse ou du moins ce n'est pas la manière fonctionnelle.

à BEAR:

Ce que je voulais dire au sujet de la sécurité est la suivante:

var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way 

//some = Some method which you don't control - it could be from another assembly which represents a crazy plugin etc. 
some(lastFive2); 
//Now what? 

Dans ces cas, vous auriez à faire une copie pour être sûr. Mais dans la plupart des cas, votre façon serait bien - et un peu plus efficace que cela pour +1 :)

Une idée est d'utiliser une file d'attente qui n'ont Enqueue interne, etc.

+0

Aïe, ça va travailler, mais ça ne va pas être efficace, ni élégant. – Mark

+0

@Mark Comme je le dis, je suis d'accord avec vous :) Je vais trouver une méthode plus efficace ... –

+0

Aussi, en fonction de ce que 'finiteList' est, il peut même être POSSIBLE de l'énumérer deux fois (ce qui votre deuxième suggestion exigerait) – Mark

3

MoreLINQ fournit un TakeLast extension method:

var last10 = finiteList.TakeLast(10); 

pour prendre les éléments d'un décalage à la fin, Enumerable.Skip devrait faire l'affaire:

var allFromOffsetToEnd = finiteList.Skip(offset); 
+0

+1 PRESQUE exactement ce que je suis venu - un peu plus tard que votre message;) –

1

une note sur pe performance. Beaucoup de réponses opèrent sur IEnumerable<> et c'est probablement ce dont vous avez besoin et que vous devriez utiliser.

Mais si les ensembles de données sont grandes et de type List<> ou similaire, vous pouvez éviter beaucoup de itérer inutiles avec quelque chose comme:

// demo, no errorhandling 
public static IEnumerable<T> TakeFrom<T>(this IList<T> list, int offset) 
{ 
    for (int i = offset; i < list.Count; i += 1) 
    { 
     yield return list[i]; 
    } 
} 
+0

+1 Et comme un commentaire, la plupart du code framework .Net (y compris les méthodes LINQ) vérifie si les arguments sont des listes, des tableaux ou simplement IEnumerables et alors la sortie la réponse le plus efficace :) Une méthode combinée serait bien ... –

+0

@lassee: Je me demandais si cela pouvait fonctionner automatiquement par résolution de surcharge ou que vous deviez écrire un wrapper qui utilise is/as. –

+0

La surcharge le ferait, mais un avantage avec l'approche is/as est que si vous donnez une source, vous n'avez pas de liste, alors vous devrez faire le/comme vous-même. –

2

@lasseespeholt:

public static class EnumerableExtensions 
{ 
    public static ReadOnlyEnumerable<T> AsReadOnly<T>(
     this IEnumerable<T> source) 
    { 
     return new ReadOnlyEnumerable<T>(source); 
    } 
} 

public sealed class ReadOnlyEnumerable<T> : IEnumerable<T> 
{ 
    private readonly IEnumerable<T> _source; 

    public ReadOnlyEnumerable(IEnumerable<T> source) 
    { 
     if (_source == null) 
     { 
      throw new ArgumentNullException("source"); 
     } 

     _source = source; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _source.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return _source.GetEnumerator(); 
    } 
} 

public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (count < 0) throw new ArgumentOutOfRangeException("count"); 

    if (count == 0) 
     return Enumerable.Empty<T>(); 

    var queue = new Queue<T>(count); 

    foreach (var t in source) 
    { 
     if (queue.Count == count) queue.Dequeue(); 

     queue.Enqueue(t); 
    } 

    return queue.AsReadOnly(); 
} 
+0

+1 Pour l'efficacité ... mais regardez ma réponse mise à jour –

+0

Ta, merci beaucoup :) –

+0

Qu'est-ce que ReadOnlyEnumerable exactement? Les interfaces 'IEnumerable' /' IEnumerable 'sont déjà en lecture seule. – LukeH

Questions connexes