2012-04-20 11 views
14

Uso de Linq; ¿Cómo puedo hacer lo "opuesto" a Take?Linq: ¿El "opuesto" de Take?

I.e. en lugar de obtener los primeros n elementos, como en

aCollection.Take(n) 

Quiero obtener todo excepto los últimos n elementos. Algo así como

aCollection.Leave(n) 

(No pregunte por qué :-)

Editar

supongo que puedo hacerlo de esta manera aCollection.TakeWhile((x, index) => index < aCollection.Count - n) o en la forma de una extensión

public static IEnumerable<TSource> Leave<TSource>(this IEnumerable<TSource> source, int n) 
{ 
    return source.TakeWhile((x, index) => index < source.Count() - n); 
} 

Pero en el caso de Linq a SQL o NHibernate Linq habría sido nic e si el SQL generado se encargó de eso y generó algo así como (para SQL Server/T-SQL)

SELECT TOP(SELECT COUNT(*) [email protected] FROM ATable) * FROM ATable O alguna otra implementación de SQL más inteligente.

Supongo que no hay nada como eso? (. Sin embargo, la edición fue en realidad no es parte de la pregunta)

+0

Nota: Esta no es la misma que Skip ! –

+0

Wow, ustedes son rápidos! Parece que la respuesta de Shedal y Jim Dagg es la mejor para mi propósito. ¿Cómo sé cuál de estos seleccionar como respuesta correcta? Jim fue más rápido, pero Shedal obtuvo los votos. –

+0

Acepta la respuesta de Shedal. Reverse no funcionará en la base de datos, ya que la base de datos necesita un calificador en el campo que desea clasificar, tiene un error de tiempo de ejecución cuando lo probaba en ORM. Probé el código de Shedal en ORM, su código funciona bien –

Respuesta

21
aCollection.Take(aCollection.Count() - n); 

EDIT: del mismo modo que una pieza de información interesante que le había salido en los comentarios - usted puede pensar que el método de extensión IEnumerable 's .Count() es lento, porque sería iterar a través de todos los elementos. Pero en caso de que el objeto real implemente ICollection o ICollection<T>, solo usará la propiedad .Count que debería ser O (1). Entonces el rendimiento no sufrirá en ese caso.

Puedes ver el código fuente de IEnumerable.Count()at TypeDescriptor.net.

+0

¿Quiere decir 'aCollection.Count()'? 'Length' es una propiedad de matrices. – Oded

+0

@Oded gracias, actualizado. –

+0

que toma al final tampoco es necesario –

11

Estoy bastante seguro de que no hay método integrado para esto, pero esto se puede hacer fácilmente por el encadenamiento de Reverse y Skip:

aCollection.Reverse().Skip(n).Reverse() 
+2

Creo que esto es mejor: 'aCollection.Skip (aCollection.Count() - n)', suponiendo 'n Oded

+1

Evitar 2 llamadas a Reverse() a favor de 1 llamada a Count() es definitivamente el camino a seguir – riwalk

+1

@Oded: Estoy de acuerdo, y ya he votado por encima de la respuesta de Shedal. Mantendré el mío aquí de todos modos como una solución alternativa. – Heinzi

5

No creo que haya una función incorporada para esto.

aCollection.Take(aCollection.Count - n)

debería ser adecuado; tomando el número total de elementos en la colección, menos n debería omitir los últimos n elementos.

1

Esto será mucho más eficiente que las soluciones con doble inversión, ya que crea solo una lista y solo enumera la lista una vez.

public static class Extensions 
{ 
    static IEnumerable<T> Leave<T>(this IEnumerable<T> items, int numToSkip) 
    { 
     var list = items.ToList(); 
     // Assert numToSkip <= list count. 
     list.RemoveRange(list.Count - numToSkip, numToSkip); 
     return List 
    } 
} 


string alphabet = "abcdefghijklmnopqrstuvwxyz"; 
var chars = alphabet.Leave(10); // abcdefghijklmnop 
1

Siguiendo con la filosofía IEnumerable, y pasando por la enumeración de una vez para los casos en que ICollection no está implementado, puede utilizar estos métodos de extensión:

public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop); 

public static IEnumerable<T> Leave<T>(this IEnumerable<T> src, int drop) { 
    var esrc = src.GetEnumerator(); 
    var buf = new Queue<T>(); 
    while (drop-- > 0) 
     if (esrc.MoveNext()) 
      buf.Enqueue(esrc.Current); 
     else 
      break; 

    while (esrc.MoveNext()) { 
     buf.Enqueue(esrc.Current); 
     yield return buf.Dequeue(); 
    } 
}