2009-03-25 7 views
86

Estoy usando .NET 3.5 y me gustaría poder obtener cada * n * th elemento de una lista. No me molesta si se logra usando una expresión lambda o LINQ.¿Cómo puedo obtener cada enésimo artículo de una lista <T>?

Editar

Parece que esta pregunta provocó mucho debate (que es una buena cosa, ¿verdad?). Lo principal que aprendí es que cuando crees que sabes todas las formas de hacer algo (incluso tan simple como esto), ¡piénsalo de nuevo!

+0

No he editado ningún significado detrás de su pregunta original; Solo lo limpié y usé Capitalización y puntuación correctamente. (.NET está en mayúscula, LINQ está en mayúsculas y no es una 'lambda', es una 'expresión lambda'). –

+1

Has reemplazado "fussed" por "sure", que no son en absoluto sinónimos. – mquander

+0

¿Quiere decir 'quisquilloso'? –

Respuesta

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

@mquander: Tenga en cuenta que esto realmente le dará el n-ésimo elemento. Si desea los elementos enésimos reales (omitiendo el primero), tendrá que agregar 1 a i. – casperOne

+1

Sí, supongo que de alguna manera depende de lo que quiere decir "enésimo", pero su interpretación podría ser más común. Sumar o restar de i para satisfacer sus necesidades. – mquander

+5

Solo para tener en cuenta: la solución Linq/Lambda tendrá un rendimiento mucho menor que un simple bucle con incremento fijo. – MartinStettner

35

Sé que es "vieja escuela", pero ¿por qué no utilizar un bucle for con step = n?

+0

Eso fue básicamente mi pensamiento. –

+0

@Michael Todd: Funciona, pero el problema es que tiene que duplicar esa funcionalidad en todas partes. Al usar LINQ, se convierte en parte de la consulta compuesta. – casperOne

+0

No creo que ese sea el punto de esta pregunta, aunque – Sung

10

de bucles

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

El conteo evaluará el enumerable. si esto se hizo de manera amigable, entonces podría evaluar y tomar el primer valor 100, por ejemplo, '' source.TakeEvery (5) .Take (100) '' ' Si la fuente subyacente era cara de evaluar, entonces su el acercamiento causaría que cada elemento sea evaluado – RhysC

30

Suena como

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

haría el truco. No veo la necesidad de usar Linq o expresiones lambda.

EDIT:

Que sea

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

y escribir de una manera LINQish

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

segunda edición:

para que sea aún más LINQis h

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

Me gusta este método para usar el método get [] en lugar del método Where(), que itera esencialmente cada elemento de IEnumerable. Si tiene un tipo IList/ICollection, este es el mejor enfoque, en mi humilde opinión. – spoulson

+0

No estoy seguro de cómo funciona la lista, pero ¿por qué utiliza un bucle y devuelve 'list [i]' sino que simplemente devuelve 'list [n-1]'? –

+0

@JuanCarlosOropeza devuelve cada enésimo elemento (por ejemplo, 0, 3, 6 ...), no solo el enésimo elemento de la lista. – alfoks

23

Puede utilizar el caso de sobrecarga de la cual pasa el índice junto con el elemento

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

Tengo que decir que soy un fan de este método. –

+1

Sigo olvidando que puedes hacer eso, muy agradable. –

3

No estoy seguro si es posible hacerlo con una expresión LINQ, pero sé que pueda use el método de extensión Where para hacerlo. Por ejemplo, para obtener cada quinto elemento:

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

Esto obtendrá el primer elemento y cada quinto desde allí. Si desea comenzar en el quinto elemento en lugar del primero, compare con 4 en lugar de comparar con 0.

1

Creo que si proporciona una extensión linq, debería poder operar en la interfaz menos específica, por lo tanto en IEnumerable. Por supuesto, si está buscando velocidad, especialmente para N grandes, puede proporcionar una sobrecarga para el acceso indexado. Este último elimina la necesidad de iterar sobre grandes cantidades de datos no necesarios, y será mucho más rápido que la cláusula Where. Al proporcionar ambas sobrecargas, el compilador puede seleccionar la variante más adecuada.

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

Esto funciona para cualquier lista? porque trato de usar en una lista para una clase personalizada y devolver un IEnumarted en lugar de y forzar coversion (clase) List.GetNth (1) tampoco funciona. –

+0

Fue mi culpa Tengo que incluir 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(); 
} 

salida

enter image description here

0

En mi humilde opinión hay una respuesta es correcta. Todas las soluciones comienzan desde 0. Pero quiero tener el n-elemento real

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]; 
} 
Cuestiones relacionadas