¿Dividir una colección en partes `n` con LINQ?

122

¿Hay una buena manera de dividir una colección en npartes con LINQ? No necesariamente de manera uniforme, por supuesto.

Es decir, quiero dividir la colección en subcolecciones, cada una de las cuales contiene un subconjunto de los elementos, donde la última colección puede ser desigual.

Simon_Weaver
fuente
1
Etiquetado: la pregunta no tiene nada que ver con asp.net. Por favor etiquete sus preguntas apropiadamente.
¿Cómo exactamente quieres que se dividan, si no incluso (permitiendo el final, por supuesto)?
Marc Gravell
1
¿Quién se vinculó a esta pregunta? John, ¿eras tú? :-) de repente todas estas respuestas :-)
Simon_Weaver
Pregunta relacionada Dividir la lista en sublistas con LINQ
Gennady Vanin Геннадий Ванин
@Simon_Weaver Intenté aclarar lo que estás preguntando en función de la respuesta aceptada. De hecho, hay muchas formas de 'dividir' una lista, incluida la descomposición de cada elemento de la lista en sus elementos, y ponerlos en las llamadas listas 'paralelas'.
jpaugh

Respuestas:

127

Un linq puro y la solución más simple es como se muestra a continuación.

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int i = 0;
        var splits = from item in list
                     group item by i++ % parts into part
                     select part.AsEnumerable();
        return splits;
    }
}
Muhammad Hasan Khan
fuente
3
Puede hacer: seleccionar part.AsEnumerable () en lugar de select (IEnumerable <T>) part. Se siente más elegante.
tuinstoel
2
Hacer todas esas operaciones de módulo puede ser un poco costoso en listas largas.
Jonathan Allen
8
Sería mejor usar la sobrecarga Select que incluye el índice.
Marc Gravell
1
Agregué
1
.AsEnumerable()no es necesario, IGrouping <T> ya es un IEnumerable <T>.
Alex
58

EDITAR: Bien, parece que leí mal la pregunta. Lo leí como "piezas de longitud n" en lugar de "n piezas". Doh! Considerando eliminar la respuesta ...

(Respuesta original)

No creo que haya una forma integrada de particionar, aunque tengo la intención de escribir una en mi conjunto de adiciones a LINQ to Objects. Marc Gravell tiene una implementación aquí, aunque probablemente lo modificaría para devolver una vista de solo lectura:

public static IEnumerable<IEnumerable<T>> Partition<T>
    (this IEnumerable<T> source, int size)
{
    T[] array = null;
    int count = 0;
    foreach (T item in source)
    {
        if (array == null)
        {
            array = new T[size];
        }
        array[count] = item;
        count++;
        if (count == size)
        {
            yield return new ReadOnlyCollection<T>(array);
            array = null;
            count = 0;
        }
    }
    if (array != null)
    {             
        Array.Resize(ref array, count);
        yield return new ReadOnlyCollection<T>(array);
    }
}
Jon Skeet
fuente
Maldición
Marc Gravell
3
Usted realmente no lo hace como los "array [recuento ++]", eh ;-P
Marc Gravell
18
Gracias por no eliminar, aunque no es una respuesta para el OP, quería exactamente lo mismo: piezas de longitud n :).
Gishu
2
@Dejan: No, no lo hace. Tenga en cuenta el uso de yield return. Requiere un lote para estar en la memoria a la vez, pero eso es todo.
Jon Skeet
1
@Dejan: Correcto, para ser honesto, no me gustaría adivinar cómo interactúa con la partición Parallel LINQ :)
Jon Skeet
39
static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
            return list.Select((item, index) => new {index, item})
                       .GroupBy(x => x.index % parts)
                       .Select(x => x.Select(y => y.item));
    }
}
reustmd
fuente
28
Tengo una aversión irracional por el estilo SQL de Linq, así que esta es mi respuesta favorita.
piedar
1
@ manu08, he intentado tu código, tengo una lista var dept = {1,2,3,4,5}. Después de dividir el resultado es como dept1 = {1,3,5}y dept2 = { 2,4 }dónde parts = 2. Pero el resultado que necesito es dept1 = {1,2,3}ydept2 = {4,5}
Karthik Arthik
3
Tuve el mismo problema con el módulo, así que calculé la longitud de la columna y int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);luego hice la división con .GroupBy(x => x.index / columnLength). Una desventaja es Count () enumera la lista.
goodeye 03 de
24

Ok, arrojaré mi sombrero al ring. Las ventajas de mi algoritmo:

  1. Sin costosos operadores de multiplicación, división o módulo
  2. Todas las operaciones son O (1) (ver nota a continuación)
  3. Funciona para la fuente IEnumerable <> (no se necesita la propiedad Count)
  4. Sencillo

El código:

public static IEnumerable<IEnumerable<T>>
  Section<T>(this IEnumerable<T> source, int length)
{
  if (length <= 0)
    throw new ArgumentOutOfRangeException("length");

  var section = new List<T>(length);

  foreach (var item in source)
  {
    section.Add(item);

    if (section.Count == length)
    {
      yield return section.AsReadOnly();
      section = new List<T>(length);
    }
  }

  if (section.Count > 0)
    yield return section.AsReadOnly();
}

Como se señala en los comentarios a continuación, este enfoque en realidad no aborda la pregunta original que pedía un número fijo de secciones de aproximadamente la misma longitud. Dicho esto, aún puede usar mi enfoque para resolver la pregunta original llamándolo de esta manera:

myEnum.Section(myEnum.Count() / number_of_sections + 1)

Cuando se usa de esta manera, el enfoque ya no es O (1) ya que la operación Count () es O (N).

Miguel
fuente
Brillante - ¡la mejor solución aquí! Algunas optimizaciones: * Borre la lista vinculada en lugar de crear una nueva para cada sección. Una referencia a la lista vinculada nunca se devuelve a la persona que llama, por lo que es completamente segura. * No cree la lista vinculada hasta que llegue al primer elemento, de esa manera no hay asignación si la fuente está vacía
ShadowChaser
3
@ShadowChaser Según MSDN, borrar la LinkedList es una complejidad O (N), por lo que arruinaría mi objetivo de O (1). Por supuesto, podría argumentar que el foreach es O (N) para empezar ... :)
Mike
44
su respuesta es correcta, pero la pregunta es incorrecta. Su respuesta proporciona un número desconocido de fragmentos con un tamaño fijo para cada fragmento. Pero OP quiere una funcionalidad Split en la que proporcione un número fijo de fragmentos con cualquier tamaño por fragmento (con suerte de tamaños iguales o casi iguales). Quizás más adecuado aquí stackoverflow.com/questions/3773403/…
nawfal
1
@ Mike, ¿lo has comparado? Espero que sepa que O (1) no significa más rápido, solo significa que el tiempo requerido para la partición no escala. Me pregunto cuál es su razón para apegarse ciegamente a O (1) cuando puede ser más lento que otros O (n) para todos los escenarios de la vida real. Incluso lo probé para una loca lista de fuerza de 10 ^ 8 y la mía parecía ser aún más rápida. Espero que sepas que ni siquiera hay tipos de colección estándar que puedan contener 10 ^ 12 artículos ..
nawfal
1
@nawfal - Gracias por tu análisis detallado, me ayuda a mantenerme alerta. Las listas enlazadas en general son conocidas por inserciones finales eficientes, por eso lo seleccioné aquí. Sin embargo, solo lo comparé y, de hecho, la Lista <> es mucho más rápida. Sospecho que esto es algún tipo de detalle de implementación de .NET, que tal vez merece una pregunta por separado de StackOverflow. He modificado mi respuesta para usar la Lista <> según su sugerencia. La asignación previa de la capacidad de la lista garantiza que la inserción final sigue siendo O (1) y cumple con mi objetivo de diseño original. También cambié al .AsReadOnly () incorporado en .NET 4.5.
Mike
16

Esto es lo mismo que la respuesta aceptada, pero una representación mucho más simple:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, 
                                                   int numOfParts)
{
    int i = 0;
    return items.GroupBy(x => i++ % numOfParts);
}

El método anterior divide una IEnumerable<T>N en un número de trozos de igual tamaño o casi iguales.

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}

El método anterior divide un IEnumerable<T>trozo en trozos del tamaño fijo deseado y el número total de trozos no es importante, de lo que no se trata la pregunta.

El problema con el Splitmétodo, además de ser más lento, es que codifica la salida en el sentido de que la agrupación se realizará sobre la base de un múltiplo de N para cada posición, o en otras palabras, no obtendrá los fragmentos En el orden original.

Casi todas las respuestas aquí no conservan el orden, o se trata de particionar y no dividir, o simplemente es incorrecto. Pruebe esto, que es más rápido, conserva el orden pero un poco más detallado:

public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items, 
                                                   int numberOfChunks)
{
    if (numberOfChunks <= 0 || numberOfChunks > items.Count)
        throw new ArgumentOutOfRangeException("numberOfChunks");

    int sizePerPacket = items.Count / numberOfChunks;
    int extra = items.Count % numberOfChunks;

    for (int i = 0; i < numberOfChunks - extra; i++)
        yield return items.Skip(i * sizePerPacket).Take(sizePerPacket);

    int alreadyReturnedCount = (numberOfChunks - extra) * sizePerPacket;
    int toReturnCount = extra == 0 ? 0 : (items.Count - numberOfChunks) / extra + 1;
    for (int i = 0; i < extra; i++)
        yield return items.Skip(alreadyReturnedCount + i * toReturnCount).Take(toReturnCount);
}

El método equivalente para una Partitionoperación aquí

nawfal
fuente
6

He estado usando la función Partición que publiqué anteriormente con bastante frecuencia. Lo único malo fue que no se transmitía por completo. Esto no es un problema si trabaja con pocos elementos en su secuencia. Necesitaba una nueva solución cuando comencé a trabajar con más de 100.000 elementos en mi secuencia.

La siguiente solución es mucho más compleja (¡y más código!), Pero es muy eficiente.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace LuvDaSun.Linq
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int partitionSize)
        {
            /*
            return enumerable
                .Select((item, index) => new { Item = item, Index = index, })
                .GroupBy(item => item.Index / partitionSize)
                .Select(group => group.Select(item => item.Item)                )
                ;
            */

            return new PartitioningEnumerable<T>(enumerable, partitionSize);
        }

    }


    class PartitioningEnumerable<T> : IEnumerable<IEnumerable<T>>
    {
        IEnumerable<T> _enumerable;
        int _partitionSize;
        public PartitioningEnumerable(IEnumerable<T> enumerable, int partitionSize)
        {
            _enumerable = enumerable;
            _partitionSize = partitionSize;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            return new PartitioningEnumerator<T>(_enumerable.GetEnumerator(), _partitionSize);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


    class PartitioningEnumerator<T> : IEnumerator<IEnumerable<T>>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitioningEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
            _enumerator.Dispose();
        }

        IEnumerable<T> _current;
        public IEnumerable<T> Current
        {
            get { return _current; }
        }
        object IEnumerator.Current
        {
            get { return _current; }
        }

        public void Reset()
        {
            _current = null;
            _enumerator.Reset();
        }

        public bool MoveNext()
        {
            bool result;

            if (_enumerator.MoveNext())
            {
                _current = new PartitionEnumerable<T>(_enumerator, _partitionSize);
                result = true;
            }
            else
            {
                _current = null;
                result = false;
            }

            return result;
        }

    }



    class PartitionEnumerable<T> : IEnumerable<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitionEnumerable(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new PartitionEnumerator<T>(_enumerator, _partitionSize);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


    class PartitionEnumerator<T> : IEnumerator<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        int _count;
        public PartitionEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
        }

        public T Current
        {
            get { return _enumerator.Current; }
        }
        object IEnumerator.Current
        {
            get { return _enumerator.Current; }
        }
        public void Reset()
        {
            if (_count > 0) throw new InvalidOperationException();
        }

        public bool MoveNext()
        {
            bool result;

            if (_count < _partitionSize)
            {
                if (_count > 0)
                {
                    result = _enumerator.MoveNext();
                }
                else
                {
                    result = true;
                }
                _count++;
            }
            else
            {
                result = false;
            }

            return result;
        }

    }
}

¡Disfrutar!

Elmer
fuente
Esta versión rompe el contrato de IEnumerator. No es válido lanzar InvalidOperationException cuando se llama a Reset; creo que muchos de los métodos de extensión LINQ se basan en este comportamiento.
ShadowChaser
1
@ShadowChaser Creo que Reset () debería lanzar una NotSupportedException y todo estaría bien. De la documentación de MSDN: "El método de reinicio se proporciona para la interoperabilidad COM. No necesariamente debe implementarse; en cambio, el implementador puede simplemente lanzar una excepción NotSupportedException".
toong
@ Toong Wow, tienes razón. No estoy seguro de cómo me perdí eso después de todo este tiempo.
ShadowChaser
Es buggy! No recuerdo exactamente, pero (por lo que recuerdo) realiza pasos no deseados y puede provocar efectos secundarios feos (con lector de datos, por ejemplo). La mejor solución está aquí (Jeppe Stig Nielsen): stackoverflow.com/questions/13709626/…
SalientBrain
4

Hilo interesante. Para obtener una versión de transmisión de Split / Partition, uno puede usar enumeradores y producir secuencias del enumerador utilizando métodos de extensión. La conversión de código imperativo a código funcional usando el rendimiento es una técnica muy poderosa.

Primero, una extensión de enumerador que convierte una cuenta de elementos en una secuencia perezosa:

public static IEnumerable<T> TakeFromCurrent<T>(this IEnumerator<T> enumerator, int count)
{
    while (count > 0)
    {
        yield return enumerator.Current;
        if (--count > 0 && !enumerator.MoveNext()) yield break;
    }
}

Y luego una extensión enumerable que divide una secuencia:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> seq, int partitionSize)
{
    var enumerator = seq.GetEnumerator();

    while (enumerator.MoveNext())
    {
        yield return enumerator.TakeFromCurrent(partitionSize);
    }
}

El resultado final es una implementación altamente eficiente, de transmisión y perezosa que se basa en un código muy simple.

¡Disfrutar!

Martin Fredriksson
fuente
Inicialmente programé lo mismo, pero el patrón se rompe cuando se llama a Reset en una de las instancias IEnumerable <T> anidadas.
ShadowChaser
1
¿Esto todavía funciona si solo enumeras la partición y no la enumerable interna? dado que el enumerador interno está diferido, ninguno de los códigos para el interno (toma de actual) se ejecutará hasta que se enumere, por lo tanto, movenext () solo será invocado por la función de partición externa, ¿verdad? Si mis suposiciones son ciertas, esto puede generar potencialmente n particiones con n elementos en el enumerable original y los enumerables internos arrojarán resultados inesperados
Brad
@Brad "fallará" como espera, similar a algunos de los problemas en este hilo stackoverflow.com/questions/419019/… (específicamente stackoverflow.com/a/20953521/1037948 )
drzaus
4

Yo uso esto:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> instance, int partitionSize)
{
    return instance
        .Select((value, index) => new { Index = index, Value = value })
        .GroupBy(i => i.Index / partitionSize)
        .Select(i => i.Select(i2 => i2.Value));
}
Elmer
fuente
Por favor explique por qué. ¡He estado usando esta función sin ningún problema!
Elmer
lea la pregunta nuevamente y vea si obtiene n (casi) partes de igual longitud con su función
Muhammad Hasan Khan
@Elmer, su respuesta es correcta, pero la pregunta es incorrecta. Su respuesta proporciona un número desconocido de fragmentos con un tamaño fijo para cada fragmento (exactamente como Partición, el nombre que le ha asignado). Pero OP quiere una funcionalidad Split en la que proporcione un número fijo de fragmentos con cualquier tamaño por fragmento (con suerte de tamaños iguales o casi iguales). Quizás más adecuado aquí stackoverflow.com/questions/3773403/…
nawfal
Creo que puede cambiar i.Index / divisionSize a i.Index% divisionSize y obtener el resultado solicitado. También prefiero esto sobre la respuesta aceptada, ya que es más compacta y legible.
Jake Drew
2

Esto es eficiente en memoria y difiere la ejecución tanto como sea posible (por lote) y opera en tiempo lineal O (n)

    public static IEnumerable<IEnumerable<T>> InBatchesOf<T>(this IEnumerable<T> items, int batchSize)
    {
        List<T> batch = new List<T>(batchSize);
        foreach (var item in items)
        {
            batch.Add(item);

            if (batch.Count >= batchSize)
            {
                yield return batch;
                batch = new List<T>();
            }
        }

        if (batch.Count != 0)
        {
            //can't be batch size or would've yielded above
            batch.TrimExcess();
            yield return batch;
        }
    }
Puntilla
fuente
2

Hay muchas respuestas geniales para esta pregunta (y sus primos). Lo necesitaba yo mismo y había creado una solución diseñada para ser eficiente y tolerante a errores en un escenario donde la colección de origen puede tratarse como una lista. No utiliza ninguna iteración perezosa, por lo que puede no ser adecuado para colecciones de tamaño desconocido que pueden aplicar presión de memoria.

static public IList<T[]> GetChunks<T>(this IEnumerable<T> source, int batchsize)
{
    IList<T[]> result = null;
    if (source != null && batchsize > 0)
    {
        var list = source as List<T> ?? source.ToList();
        if (list.Count > 0)
        {
            result = new List<T[]>();
            for (var index = 0; index < list.Count; index += batchsize)
            {
                var rangesize = Math.Min(batchsize, list.Count - index);
                result.Add(list.GetRange(index, rangesize).ToArray());
            }
        }
    }
    return result ?? Enumerable.Empty<T[]>().ToList();
}

static public void TestGetChunks()
{
    var ids = Enumerable.Range(1, 163).Select(i => i.ToString());
    foreach (var chunk in ids.GetChunks(20))
    {
        Console.WriteLine("[{0}]", String.Join(",", chunk));
    }
}

He visto algunas respuestas en esta familia de preguntas que usan GetRange y Math.Min. Pero creo que, en general, esta es una solución más completa en términos de verificación de errores y eficiencia.

rhaben
fuente
1
   protected List<List<int>> MySplit(int MaxNumber, int Divider)
        {
            List<List<int>> lst = new List<List<int>>();
            int ListCount = 0;
            int d = MaxNumber / Divider;
            lst.Add(new List<int>());
            for (int i = 1; i <= MaxNumber; i++)
            {
                lst[ListCount].Add(i);
                if (i != 0 && i % d == 0)
                {
                    ListCount++;
                    d += MaxNumber / Divider;
                    lst.Add(new List<int>());
                }
            }
            return lst;
        }
Amit Sengar
fuente
1

Grandes respuestas, para mi escenario probé la respuesta aceptada, y parece que no mantiene el orden. También hay una gran respuesta de Nawfal que mantiene el orden. Pero en mi caso, quería dividir el resto de una manera normalizada, todas las respuestas que vi difundieron el resto o al principio o al final.

Mi respuesta también toma que el resto se extienda de manera más normalizada.

 static class Program
{          
    static void Main(string[] args)
    {
        var input = new List<String>();
        for (int k = 0; k < 18; ++k)
        {
            input.Add(k.ToString());
        }
        var result = splitListIntoSmallerLists(input, 15);            
        int i = 0;
        foreach(var resul in result){
            Console.WriteLine("------Segment:" + i.ToString() + "--------");
            foreach(var res in resul){
                Console.WriteLine(res);
            }
            i++;
        }
        Console.ReadLine();
    }

    private static List<List<T>> splitListIntoSmallerLists<T>(List<T> i_bigList,int i_numberOfSmallerLists)
    {
        if (i_numberOfSmallerLists <= 0)
            throw new ArgumentOutOfRangeException("Illegal value of numberOfSmallLists");

        int normalizedSpreadRemainderCounter = 0;
        int normalizedSpreadNumber = 0;
        //e.g 7 /5 > 0 ==> output size is 5 , 2 /5 < 0 ==> output is 2          
        int minimumNumberOfPartsInEachSmallerList = i_bigList.Count / i_numberOfSmallerLists;                        
        int remainder = i_bigList.Count % i_numberOfSmallerLists;
        int outputSize = minimumNumberOfPartsInEachSmallerList > 0 ? i_numberOfSmallerLists : remainder;
        //In case remainder > 0 we want to spread the remainder equally between the others         
        if (remainder > 0)
        {
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                normalizedSpreadNumber = (int)Math.Floor((double)i_numberOfSmallerLists / remainder);    
            }
            else
            {
                normalizedSpreadNumber = 1;
            }   
        }
        List<List<T>> retVal = new List<List<T>>(outputSize);
        int inputIndex = 0;            
        for (int i = 0; i < outputSize; ++i)
        {
            retVal.Add(new List<T>());
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                retVal[i].AddRange(i_bigList.GetRange(inputIndex, minimumNumberOfPartsInEachSmallerList));
                inputIndex += minimumNumberOfPartsInEachSmallerList;
            }
            //If we have remainder take one from it, if our counter is equal to normalizedSpreadNumber.
            if (remainder > 0)
            {
                if (normalizedSpreadRemainderCounter == normalizedSpreadNumber-1)
                {
                    retVal[i].Add(i_bigList[inputIndex]);
                    remainder--;
                    inputIndex++;
                    normalizedSpreadRemainderCounter=0;
                }
                else
                {
                    normalizedSpreadRemainderCounter++;
                }
            }
        }
        return retVal;
    }      

}
Robocida
fuente
0

Si el orden en estas partes no es muy importante, puede intentar esto:

int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int n = 3;

var result =
   array.Select((value, index) => new { Value = value, Index = index }).GroupBy(i => i.Index % n, i => i.Value);

// or
var result2 =
   from i in array.Select((value, index) => new { Value = value, Index = index })
   group i.Value by i.Index % n into g
   select g;

Sin embargo, estos no se pueden transmitir a IEnumerable <IEnumerable <int>> por alguna razón ...

okutane
fuente
Se puede hacer. En lugar de la conversión directa, simplemente haga que la función sea genérica y luego
instálela
0

Este es mi código, bonito y corto.

 <Extension()> Public Function Chunk(Of T)(ByVal this As IList(Of T), ByVal size As Integer) As List(Of List(Of T))
     Dim result As New List(Of List(Of T))
     For i = 0 To CInt(Math.Ceiling(this.Count / size)) - 1
         result.Add(New List(Of T)(this.GetRange(i * size, Math.Min(size, this.Count - (i * size)))))
     Next
     Return result
 End Function
Jonathan Allen
fuente
0

Este es mi camino, enumerando artículos y dividiendo filas por columnas

  int repat_count=4;

  arrItems.ForEach((x, i) => {
    if (i % repat_count == 0) 
        row = tbo.NewElement(el_tr, cls_min_height);
    var td = row.NewElement(el_td);
    td.innerHTML = x.Name;
  });
IlPADLI
fuente
0

Estaba buscando una división como la que tiene cadena, por lo que toda la Lista se divide de acuerdo con alguna regla, no solo la primera parte, esta es mi solución

List<int> sequence = new List<int>();
for (int i = 0; i < 2000; i++)
{
     sequence.Add(i);
}
int splitIndex = 900;
List<List<int>> splitted = new List<List<int>>();
while (sequence.Count != 0)
{
    splitted.Add(sequence.Take(splitIndex).ToList() );
    sequence.RemoveRange(0, Math.Min(splitIndex, sequence.Count));
}
Adel
fuente
La próxima vez intente: var nrs = Enumerable.Range (1,2000) .ToList ();
MBoros
0

Aquí hay un pequeño ajuste para la cantidad de elementos en lugar de la cantidad de partes:

public static class MiscExctensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int nbItems)
    {
        return (
            list
            .Select((o, n) => new { o, n })
            .GroupBy(g => (int)(g.n / nbItems))
            .Select(g => g.Select(x => x.o))
        );
    }
}
JB
fuente
-1
int[] items = new int[] { 0,1,2,3,4,5,6,7,8,9, 10 };

int itemIndex = 0;
int groupSize = 2;
int nextGroup = groupSize;

var seqItems = from aItem in items
               group aItem by 
                            (itemIndex++ < nextGroup) 
                            ? 
                            nextGroup / groupSize
                            :
                            (nextGroup += groupSize) / groupSize
                            into itemGroup
               select itemGroup.AsEnumerable();

fuente
-1

Acabo de encontrar este hilo, y la mayoría de las soluciones aquí implican agregar elementos a las colecciones, materializando efectivamente cada página antes de devolverla. Esto es malo por dos razones: en primer lugar, si sus páginas son grandes, hay una sobrecarga de memoria para llenar la página, en segundo lugar, hay iteradores que invalidan los registros anteriores cuando avanza al siguiente (por ejemplo, si ajusta un DataReader dentro de un método de enumerador) .

Esta solución utiliza dos métodos de enumerador anidados para evitar la necesidad de almacenar en caché los elementos en colecciones temporales. Dado que los iteradores externo e interno atraviesan el mismo enumerable, necesariamente comparten el mismo enumerador, por lo que es importante no avanzar el externo hasta que haya terminado de procesar la página actual. Dicho esto, si decide no iterar por toda la página actual, cuando pase a la página siguiente, esta solución iterará hacia adelante hasta el límite de la página automáticamente.

using System.Collections.Generic;

public static class EnumerableExtensions
{
    /// <summary>
    /// Partitions an enumerable into individual pages of a specified size, still scanning the source enumerable just once
    /// </summary>
    /// <typeparam name="T">The element type</typeparam>
    /// <param name="enumerable">The source enumerable</param>
    /// <param name="pageSize">The number of elements to return in each page</param>
    /// <returns></returns>
    public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int pageSize)
    {
        var enumerator = enumerable.GetEnumerator();

        while (enumerator.MoveNext())
        {
            var indexWithinPage = new IntByRef { Value = 0 };

            yield return SubPartition(enumerator, pageSize, indexWithinPage);

            // Continue iterating through any remaining items in the page, to align with the start of the next page
            for (; indexWithinPage.Value < pageSize; indexWithinPage.Value++)
            {
                if (!enumerator.MoveNext())
                {
                    yield break;
                }
            }
        }
    }

    private static IEnumerable<T> SubPartition<T>(IEnumerator<T> enumerator, int pageSize, IntByRef index)
    {
        for (; index.Value < pageSize; index.Value++)
        {
            yield return enumerator.Current;

            if (!enumerator.MoveNext())
            {
                yield break;
            }
        }
    }

    private class IntByRef
    {
        public int Value { get; set; }
    }
}
Jon G
fuente
¡Esto no está funcionando en absoluto! ¡El mejor posible es aquí stackoverflow.com/questions/13709626/… ! Ver comentarios.
SalientBrain