2010-09-23 15 views

Respuesta

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

o

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

Existe cierta sobrecarga en hacer esto por lo que un método personalizado sería mejor.

Actualización: Un método personalizado

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; 
    } 
} 

Actualización: cambiado el código littlebit con las ideas de dtb's contestar :-)

comentario a Bear: Mira esto ejemplo:

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... 

Usted podría pote cambiar los valores de lastFive2 y, por lo tanto, ese enfoque puede ser inseguro o al menos no es la forma funcional.

de llevar

Lo que quería decir sobre el seguro es la siguiente:

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? 

En estos casos se tendría que hacer una copia para estar seguro. Pero en la mayoría de los casos, su forma sería muy bien - y un poco más eficiente que este modo :) 1

Una idea es utilizar una cola que sólo tiene interno Enqueue etc.

+0

Huy, eso funciona, pero no va a ser eficiente ni elegante. – Mark

+0

@Mark Como señalo, estoy de acuerdo contigo :) Voy a llegar a un método más eficiente ... –

+0

Además, dependiendo de lo que 'finiteList' es, puede o no ser POSIBLE enumerarlo dos veces (lo cual su segunda sugerencia requeriría) – Mark

3

MoreLINQ proporciona una TakeLast extension method:

var last10 = finiteList.TakeLast(10); 

tomar los elementos partir de un desplazamiento hasta el final, Enumerable.Skip debe hacer el truco:

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

+1 CASI exactamente lo que se me ocurrió, un poco más tarde que tu publicación;) –

1

una nota sobre la PE rendimiento Aquí encontrará muchas respuestas que operan en IEnumerable<> y eso es probablemente lo que necesita y debería usar.

Pero si los conjuntos de datos son grandes y de tipo List<> o similares, puede evitar una gran cantidad de iteración innecesaria con algo como:

// 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 Y a modo de comentario, la mayor parte del código .Net framework (incluidos los métodos LINQ) comprueba si los argumentos son listas, matrices o solo IEnumerables y luego envía la respuesta de la manera más eficiente :) Un método combinado sería bueno ... –

+0

@lassee: Me preguntaba si esto podría funcionar automáticamente por resolución de sobrecarga o que tienes que escribir un contenedor que utiliza is/as. –

+0

La sobrecarga lo haría, pero una ventaja con el enfoque is/as es que si le está dando a una fuente que no es una lista, entonces tendría que hacer el is/as usted mismo. –

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 Por eficiencia ... pero mira mi respuesta actualizada –

+0

Ta, muchas gracias :) –

+0

¿Para qué sirve exactamente 'ReadOnlyEnumerable '? Las interfaces 'IEnumerable' /' IEnumerable 'ya son de solo lectura. – LukeH

Cuestiones relacionadas