2010-07-08 8 views
7

Ho come Elenco di stringhe con dove rimuovo ogni duplicato, ora voglio filtrarlo ancora di più per ottenere gli ultimi 5 record. Come posso fare questo?Come ottenere l'ultimo record x da un elenco con Lambda

Quello che ho ottenuto finora

List<string> query = otherlist.Distinct().Select(a => a).ToList(); 
+0

Perché il 'Select (a => a)'? Non fa nulla ... – tzaman

+0

quello era il mio "manichino" per inserire l'ultimo filtro x records – Ivo

risposta

9

Non è necessario il .Select(a => a). Quello è ridondante.

È possibile ottenere gli ultimi 5 record, saltando sul resto come

List<string> query = otherlist.Distinct().ToList(); 
List<string> lastFive = query.Skip(query.Count-5).ToList(); 
+0

Skip() ha una prestazione migliore di Reverse() quindi Take(), ovviamente. –

+0

@Danny: Non tanto quanto si potrebbe pensare: la chiamata a 'ToList()' sarà tanto costosa quanto il mio primo 'Reverse()' (attraversa l'intera lista una volta), e dopo il 'Take (5)' è solo invertendo cinque elementi che sono noccioline. Inoltre, la mia strada non crea una lista intermedia. – tzaman

+0

@tzaman - "il mio modo non crea una lista intermedia" - sì lo fa; due di loro, infatti. Come pensi che funzioni Reverse? (beh, parlando strettamente crea una matrice 'T []', per gentile concessione di 'Buffer ' - ma la stessa differenza) –

4

ne dici di questo?

var lastFive = list.Reverse().Take(5).Reverse(); 

EDIT: Ecco il tutto -

var lastFiveDistinct = otherlist.Distinct() 
           .Reverse() 
           .Take(5) 
           .Reverse() 
           .ToList(); 

Si noti inoltre che non si dovrebbe chiamare query se hai una chiamata ToList() alla fine, perché poi è non un query più, è stato valutato e trasformato in una lista. Se hai solo bisogno di iterare sopra, puoi omettere la chiamata ToList() e lasciarla come IEnumerable.

+1

Mi sarei arrischiato che sarebbe stato più economico contare gli elementi quindi usare Skip and Take. L'inversione di un oggetto IEnumerable non può essere economica. – spender

+0

È la stessa complessità asintotica in entrambi i casi, ma hai ragione che saltare/prendere è probabilmente più veloce. – tzaman

+0

In realtà, modificherò la mia precedente affermazione - Non penso che ci sarà una differenza notevole nelle prestazioni. Vedi il mio commento sulla risposta di Jens. – tzaman

0
var count=list.Count(); 
var last5=list.Skip(count-5); 

EDIT:

ho perso che i dati siano Lista < T>. Questo approccio sarebbe meglio per IEnumerable < T>

5

modifica per soddisfare gli ingressi non-list, ora gestisce IEnumerable<T> e controlli se questo è un IList<T>; in caso contrario, lo memorizza tramite ToList(), il che consente di leggere solo i dati una volta (anziché .Count() e .Skip() che possono leggere i dati più volte).

Dal momento che questa è una lista, sarei propenso a scrivere un metodo di estensione che utilizza che al completo:

public static IEnumerable<T> TakeLast<T>(
      this IEnumerable<T> source, int count) 
    { 
     IList<T> list = (source as IList<T>) ?? source.ToList(); 
     count = Math.Min(count, list.Count); 
     for (int i = list.Count - count; i < list.Count; i++) 
     { 
      yield return list[i]; 
     } 
    } 
+0

Assolutamente. Dato che sai che è un elenco, questo è un approccio semplice e facilmente più efficiente. – Noldorin

+0

Tranne che non sarà una lista quando uscirà dalla chiamata 'Distinct()'; il 'ToList()' deve solo succedere una volta alla fine. – tzaman

+0

@tzaman: bene; editerà –

Problemi correlati