2009-03-25 21 views
86

Sto usando .NET 3.5 e vorrei poter ottenere ogni * n * articolo da un elenco. Non mi preoccupo se è raggiunto usando un'espressione lambda o LINQ.Come posso ottenere ogni ennesimo elemento da un elenco <T>?

Modifica

Sembra che questa domanda ha provocato un bel po 'di dibattito (che è una buona cosa, giusto?). La cosa principale che ho imparato è che quando pensi di sapere ogni modo di fare qualcosa (anche così semplice), ripensaci!

+0

Non ho modificato alcun significato dietro la tua domanda originale; L'ho solo ripulito e ho usato correttamente maiuscole e punteggiatura. (.NET è in maiuscolo, LINQ è in tutto-Caps, e non è un 'lambda', è un 'espressione lambda'.) –

+1

Hai sostituito "agitato" con "sicuro" che non sono affatto sinonimi. – mquander

+0

Intende "pignolo"? –

risposta

154
return list.Where((x, i) => i % nStep == 0); 
+1

@mquander: nota che questo ti darà effettivamente l'elemento n-1. Se si desidera l'effettivo ennesimo elemento (saltando il primo), sarà necessario aggiungere 1 a i. – casperOne

+1

Sì, suppongo che dipenda da cosa intendi per "ennesimo", ma la tua interpretazione potrebbe essere più comune. Aggiungi o sottrai da i in base alle tue esigenze. – mquander

+5

Solo per notare: la soluzione Linq/Lambda sarà molto meno performante di un semplice ciclo con incremento fisso. – MartinStettner

35

So che è "vecchia scuola", ma perché non usare un ciclo for con stepping = n?

+0

Questo era fondamentalmente il mio pensiero. –

+0

@ Michael Todd: Funziona, ma il problema è che devi duplicare questa funzionalità ovunque. Usando LINQ, diventa parte della query composta. – casperOne

+0

Non penso che questo sia il punto di questa domanda, anche se – Sung

10

Ciclo For

for(int i = 0; i < list.Count; i += n) 
    //Nth Item.. 
+0

Count valuterà l'enumerabile. se questo è stato fatto in un modo amichevole di linq, è possibile valutare pigramente e prendere il primo valore 100, ad esempio '' source.TakeEvery (5) .Take (100) '' Se la fonte sottostante era costosa da valutare, allora il tuo l'approccio causerebbe la valutazione di ogni elemento – RhysC

30

Suona come

IEnumerator<T> GetNth<T>(List<T> list, int n) { 
    for (int i=0; i<list.Count; i+=n) 
    yield return list[i] 
} 

farebbe il trucco. Non vedo la necessità di usare Linq o espressioni lambda.

EDIT:

Make it

public static class MyListExtensions { 
    public static IEnumerable<T> GetNth<T>(this List<T> list, int n) { 
    for (int i=0; i<list.Count; i+=n) 
     yield return list[i]; 
    } 
} 

e si scrive in un modo LINQish

from var element in MyList.GetNth(10) select element; 

secondo Edit:

Per rendere ancora più LINQis h

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i]; 
+2

Mi piace questo metodo per utilizzare questo [] getter invece del metodo Where(), che essenzialmente itera ogni elemento dell'IEnumerable. Se hai un tipo IList/ICollection, questo è l'approccio migliore, IMHO. – spoulson

+0

Non sai come funziona la lista, ma perché usi un ciclo e ritorni 'lista [i]' invece ritorni 'lista [n-1]'? –

+0

@JuanCarlosOropeza restituisce ogni ennesimo elemento (ad es. 0, 3, 6 ...), non solo l'ennesimo elemento della lista. – alfoks

23

È possibile utilizzare il Dove sovraccarico che passa l'indice insieme con l'elemento

var everyFourth = list.Where((x,i) => i % 4 == 0); 
+1

Devo dire che sono un fan di questo metodo. –

+1

Continuo a dimenticare che puoi farlo molto bene. –

3

non sono sicuro se è possibile fare con un espressione LINQ, ma so che è possibile utilizzare il metodo di estensione Where per farlo. Ad esempio per ottenere ogni quinto articolo:

List<T> list = originalList.Where((t,i) => (i % 5) == 0).ToList(); 

Questo otterrà il primo elemento e ogni quinto da lì. Se si desidera iniziare dal quinto elemento anziché dal primo, confrontare 4 anziché confrontare 0.

1

Penso che se si fornisce un'estensione linq, si dovrebbe essere in grado di operare sull'interfaccia meno specifica, quindi su IEnumerable. Ovviamente, se la velocità è elevata, specialmente per N di grandi dimensioni, è possibile che si verifichi un sovraccarico per l'accesso indicizzato. Quest'ultimo elimina la necessità di iterare su grandi quantità di dati non necessari e sarà molto più veloce della clausola Where. Fornire entrambi gli overload consente al compilatore di selezionare la variante più adatta.

public static class LinqExtensions 
{ 
    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) 
    { 
     if (n < 0) 
      throw new ArgumentOutOfRangeException("n"); 
     if (n > 0) 
     { 
      int c = 0; 
      foreach (var e in list) 
      { 
       if (c % n == 0) 
        yield return e; 
       c++; 
      } 
     } 
    } 
    public static IEnumerable<T> GetNth<T>(this IList<T> list, int n) 
    { 
     if (n < 0) 
      throw new ArgumentOutOfRangeException("n"); 
     if (n > 0) 
      for (int c = 0; c < list.Count; c += n) 
       yield return list[c]; 
    } 
} 
+0

Funziona con qualsiasi Lista? perché provo a utilizzare in un elenco per una classe personalizzata e restituire un IEnumarted anziché e forzare la coverion (classe) List.GetNth (1) non funziona nemmeno. –

+0

Per colpa mia devo includere GetNth (1) .FirstOrDefault(); –

0
private static readonly string[] sequence = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15".Split(','); 

static void Main(string[] args) 
{ 
    var every4thElement = sequence 
     .Where((p, index) => index % 4 == 0); 

    foreach (string p in every4thElement) 
    { 
     Console.WriteLine("{0}", p); 
    } 

    Console.ReadKey(); 
} 

uscita

enter image description here

0

Imho nessuna risposta è giusta. Tutte le soluzioni iniziano da 0. Ma voglio avere il vero elemento n.

public static IEnumerable<T> GetNth<T>(this IList<T> list, int n) 
{ 
    for (int i = n - 1; i < list.Count; i += n) 
     yield return list[i]; 
} 
Problemi correlati