2010-11-12 8 views
31

Duplicar posibles:
How to take all but the last element in a sequence using LINQ?Retirar al último elemento con LINQ

parece una tarea trivial con LINQ (y probablemente lo es), pero no puedo encontrar la manera de dejar caer la último elemento de secuencia con LINQ. Usando Take y pasando la longitud de la secuencia - 1 funciona bien, por supuesto. Sin embargo, ese enfoque parece bastante inconsecuente al encadenar múltiples LINQ en una sola línea de código.

IEnumerable<T> someList .... 

// this works fine 
var result = someList.Take(someList.Count() - 1); 


// but what if I'm chaining LINQ ? 
var result = someList.Where(...).DropLast().Select(...)......; 

// Will I have to break this up? 
var temp = someList.Where(...); 
var result = temp.Take(temp.Count() - 1).Select(...)........; 

En Python, podría hacer seq [0: -1]. Intenté pasar -1 al método Take, pero parece que no hago lo que necesito.

Respuesta

53

Se puede escribir su propia consulta LINQ operador (es decir, un extension method en IEnumerable<T>), por ejemplo:

static IEnumerable<T> WithoutLast<T>(this IEnumerable<T> source) 
{ 
    using (var e = source.GetEnumerator()) 
    { 
     if (e.MoveNext()) 
     { 
      for (var value = e.Current; e.MoveNext(); value = e.Current) 
      { 
       yield return value; 
      } 
     } 
    } 
} 

a diferencia de otros enfoques, tales como xs.Take(xs.Count() - 1), lo anterior procesará una secuencia solo una vez.

+0

Inteligente. 8 más para ir. –

+2

@ErwinMayer: Tres años después, y todavía no entiendo lo que quiere decir con su comentario. :-D – stakx

+0

@staks No estoy muy seguro;), tal vez fue sobre el número de votos ascendentes ... –

25
someList.Reverse().Skip(1).Reverse() 
+2

@LukeH, es O (n) y es una respuesta de línea como OP quiere. –

+2

Oh, ¿por qué no pienso en esto? Esta es una forma inteligente y simple de hacer lo que está disponible desde el primer momento. No estoy seguro de cuál será la implicación en el rendimiento, pero si solo necesito una forma rápida y sucia de hacerlo, este es un buen enfoque. – Kei

+1

@Saeed: es O (n) pero requiere el equivalente de cuatro pasadas a través de la secuencia. El primer ejemplo en la respuesta de Jamiec es también de una sola línea y O (n), pero eso solo necesita dos pasadas a través de la secuencia (o una sola pasada si la secuencia implementa 'ICollection ' o 'ICollection'.) – LukeH

13

Usted puede hacer fácilmente esto con un método de extensión, bien mediante la fórmula que ya ha encontrado, o tal vez una combinación de Take y Count

public static IEnumerable<T> DropLast<T>(this IEnumerable<T> enumerable) 
{ 
    return enumerable.Take(enumerable.Count()-1); 
} 
+1

¿No se compila la segunda vía? Last() devuelve T, pero excepto toma IEnumerable . ¿Me estoy perdiendo de algo? – Kei

+0

@Kei - probablemente sea cierto, no lo probé, pero algo en esa línea funcionaría. Respuesta editada – Jamiec

+10

El método 'Except' no es adecuado para esto porque también elimina los duplicados de la secuencia de origen. Si la fuente es '{1,2,3,4,5,4,3,2,1}', entonces el OP esperaría que los resultados fuesen '{1,2,3,4,5,4,3, 2} '; usar su segundo ejemplo (suponiendo que haya solucionado el error) daría '{2,3,4,5}'. – LukeH

Cuestiones relacionadas