Dividere una raccolta in "n" parti con LINQ?


122

C'è un modo carino per dividere una raccolta in nparti con LINQ? Non necessariamente in modo uniforme, ovviamente.

Cioè, voglio dividere la raccolta in sotto-raccolte, ciascuna delle quali contiene un sottoinsieme di elementi, dove l'ultima raccolta può essere irregolare.


1
Ricodificato: la domanda non ha nulla a che fare con asp.net. Si prega di contrassegnare le domande in modo appropriato.

Come vuoi che si dividano esattamente, se non addirittura (tenendo conto della fine, ovviamente)?
Marc Gravell

1
chi si è collegato a questa domanda? john eri tu? :-) all'improvviso tutte queste risposte :-)
Simon_Weaver


@ Simon_Weaver Ho provato a chiarire cosa stai chiedendo in base alla risposta accettata. In effetti, ci sono molti modi per "dividere" un elenco, inclusa la scomposizione di ogni elemento dell'elenco nei suoi elementi e l'inserimento di questi in cosiddetti elenchi "paralleli".
jpaugh

Risposte:


127

Un linq puro e la soluzione più semplice è come mostrato di seguito.

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

3
Puoi fare: selezionare part.AsEnumerable () invece di selezionare (IEnumerable <T>) parte. Sembra più elegante.
tuinstoel

2
Fare tutte quelle operazioni di modulo può diventare un po 'costoso per lunghi elenchi.
Jonathan Allen

8
Sarebbe meglio usare l'overload Select che include l'indice.
Marc Gravell

1
Ho aggiunto una risposta che utilizza la sintassi di selezione dell'overload e del concatenamento dei metodi
riutilizzata il

1
.AsEnumerable()non è necessario, IGrouping <T> è già un IEnumerable <T>.
Alex

58

EDIT: Ok, sembra che abbia letto male la domanda. Lo leggo come "pezzi di lunghezza n" piuttosto che "n pezzi". Doh! Considerando l'eliminazione della risposta ...

(Risposta originale)

Non credo che esista un modo integrato di partizionamento, anche se intendo scriverne uno nella mia serie di aggiunte a LINQ to Objects. Marc Gravell ha un'implementazione qui anche se probabilmente la modificherei per restituire una vista di sola lettura:

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

Darn - mi ha battuto ;-p
Marc Gravell

3
È davvero non lo fai come quelli "array [count ++]", eh ;-p
Marc Gravell

18
Grazie per non aver cancellato anche se non è una risposta per l'OP, volevo la stessa identica cosa: pezzi di lunghezza n :).
Gishu

2
@Dejan: No, non lo fa. Notare l'uso di yield return. Richiede che un batch sia in memoria alla volta, ma questo è tutto.
Jon Skeet

1
@Dejan: Giusto - Non vorrei indovinare come interagisce con il partizionamento LINQ parallelo, ad essere onesti :)
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));
    }
}

28
Ho un'irrazionale antipatia per Linq in stile SQL, quindi questa è la mia risposta preferita.
piedar

1
@ manu08, ho provato il tuo codice, ho una lista var dept = {1,2,3,4,5}. Dopo aver diviso il risultato è come dept1 = {1,3,5}e dept2 = { 2,4 }dove parts = 2. Ma il risultato di cui ho bisogno è dept1 = {1,2,3}edept2 = {4,5}
Karthik Arthik

3
Ho avuto lo stesso problema con il modulo, quindi ho calcolato la lunghezza della colonna con int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);poi ho fatto la divisione con .GroupBy(x => x.index / columnLength). Uno svantaggio è che Count () enumera l'elenco.
Goodeye

24

Ok, lancio il mio cappello sul ring. I vantaggi del mio algoritmo:

  1. Nessun operatore di moltiplicazione, divisione o modulo costoso
  2. Tutte le operazioni sono O (1) (vedere la nota sotto)
  3. Funziona per IEnumerable <> source (nessuna proprietà Count necessaria)
  4. Semplice

Il codice:

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();
}

Come sottolineato nei commenti seguenti, questo approccio in realtà non risponde alla domanda originale che richiedeva un numero fisso di sezioni di lunghezza approssimativamente uguale. Detto questo, puoi ancora usare il mio approccio per risolvere la domanda originale chiamandola in questo modo:

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

Quando viene utilizzato in questo modo, l'approccio non è più O (1) poiché l'operazione Count () è O (N).


Brillante - la migliore soluzione qui! Alcune ottimizzazioni: * Cancella l'elenco collegato invece di crearne uno nuovo per ogni sezione. Un riferimento all'elenco collegato non viene mai restituito al chiamante, quindi è completamente sicuro. * Non creare l'elenco collegato fino a quando non raggiungi il primo elemento - in questo modo non c'è allocazione se la fonte è vuota
ShadowChaser

3
@ShadowChaser Secondo MSDN, la cancellazione della LinkedList è di complessità O (N), quindi rovinerebbe il mio obiettivo di O (1). Certo, potresti sostenere che il foreach è O (N) per cominciare ... :)
Mike

4
la tua risposta è giusta, ma la domanda è sbagliata. La tua risposta fornisce un numero sconosciuto di blocchi con dimensioni fisse per ogni blocco. Ma OP vuole una funzionalità Split in cui fornisce un numero fisso di blocchi con qualsiasi dimensione per blocco (si spera di dimensioni uguali o vicine alla stessa). Forse più adatto qui stackoverflow.com/questions/3773403/...
Nawfal

1
@ Mike l'hai confrontato? Spero che tu sappia O (1) non significa più veloce, significa solo che il tempo richiesto per il partizionamento non è scalabile. Mi chiedo solo qual è la tua logica per attenersi ciecamente a O (1) quando può essere più lento di altri O (n) per tutti gli scenari di vita reale. L'ho anche testato per un folle elenco di forza 10 ^ 8 e il mio sembrava essere ancora più veloce. Spero che tu sappia che non ci sono nemmeno tipi di raccolta standard che possono contenere 10 ^ 12 articoli ..
nawfal

1
@nawfal - Grazie per la tua analisi dettagliata, mi aiuta a tenermi all'erta. Gli elenchi collegati in generale sono noti per gli inserti finali efficienti, motivo per cui l'ho selezionato qui. Tuttavia l'ho appena confrontato e in effetti List <> è molto più veloce. Sospetto che questo sia una sorta di dettaglio di implementazione .NET, forse meritevole di una domanda StackOverflow separata. Ho modificato la mia risposta per utilizzare List <> secondo il tuo suggerimento. La preallocazione della capacità dell'elenco garantisce che l'inserimento finale sia ancora O (1) e soddisfi il mio obiettivo di progettazione originale. Sono anche passato al .AsReadOnly () integrato in .NET 4.5.
Mike

16

Questa è la stessa della risposta accettata, ma una rappresentazione molto più semplice:

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

Il metodo precedente divide un IEnumerable<T>in un numero di blocchi di dimensioni uguali o vicine a dimensioni uguali.

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

Il metodo sopra divide un IEnumerable<T>in blocchi della dimensione fissa desiderata con il numero totale di blocchi non importante, che non è ciò di cui si tratta.

Il problema con il Splitmetodo, oltre ad essere più lento, è che rimescola l'output nel senso che il raggruppamento sarà fatto sulla base dell'i-esimo multiplo di N per ogni posizione, o in altre parole non si ottengono i blocchi nell'ordine originale.

Quasi ogni risposta qui o non conserva l'ordine, o riguarda il partizionamento e non la divisione, o è chiaramente sbagliata. Prova questo che è più veloce, preserva l'ordine ma un po 'più prolisso:

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

Il metodo equivalente per Partitionun'operazione qui


6

Ho usato abbastanza spesso la funzione Partizione che ho pubblicato in precedenza. L'unica cosa negativa era che non era completamente in streaming. Questo non è un problema se lavori con pochi elementi nella sequenza. Avevo bisogno di una nuova soluzione quando ho iniziato a lavorare con oltre 100.000 elementi nella mia sequenza.

La seguente soluzione è molto più complessa (e più codice!), Ma è molto efficiente.

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

    }
}

Godere!


Questa versione rompe il contratto di IEnumerator. Non è valido generare InvalidOperationException quando viene chiamato Reset: credo che molti dei metodi di estensione LINQ si basino su questo comportamento.
ShadowChaser

1
@ShadowChaser Penso che Reset () dovrebbe generare una NotSupportedException e tutto andrebbe bene. Dalla documentazione MSDN: "Il metodo Reset viene fornito per l'interoperabilità COM. Non è necessario che sia implementato; l'implementatore può invece semplicemente generare un'eccezione NotSupportedException."
Toong

@toong Wow, hai ragione. Non sono sicuro di come mi sia perso dopo tutto questo tempo.
ShadowChaser

È bacato! Non ricordo esattamente, ma (per quanto ricordo) esegue passaggi indesiderati e può portare a brutti effetti collaterali (con datareader per es.). La soluzione migliore è qui (Jeppe Stig Nielsen): stackoverflow.com/questions/13709626/...
SalientBrain

4

Filo interessante. Per ottenere una versione in streaming di Split / Partition, è possibile utilizzare enumeratori e produrre sequenze dall'enumeratore utilizzando metodi di estensione. Convertire il codice imperativo in codice funzionale utilizzando il rendimento è davvero una tecnica molto potente.

Innanzitutto un'estensione dell'enumeratore che trasforma un conteggio di elementi in una sequenza pigra:

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

E poi un'estensione enumerabile che partiziona una sequenza:

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

Il risultato finale è un'implementazione altamente efficiente, in streaming e pigra che si basa su un codice molto semplice.

Godere!


Inizialmente ho programmato la stessa cosa, ma il pattern si interrompe quando Reset viene chiamato su una delle istanze IEnumerable <T> nidificate.
ShadowChaser

1
Funziona ancora se si enumera solo la partizione e non l'enumerabile interno? poiché l'enumeratore interno è differito, nessuno del codice per l'interno (prende dalla corrente) verrà eseguito fino a quando non viene enumerato, quindi movenext () sarà chiamato solo dalla funzione di partizione esterna, giusto? Se le mie ipotesi sono vere, questo può potenzialmente produrre n partizioni con n elementi nell'enumerabile originale e gli enumerabili interni produrranno risultati inaspettati
Brad

@Brad sarà "fail", come ci si aspetta, simile ad alcuni dei problemi in questa discussione stackoverflow.com/questions/419019/... (in particolare stackoverflow.com/a/20953521/1037948 )
drzaus

4

Io uso questo:

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

Per favore, spiega perché. Ho utilizzato questa funzione senza problemi!
Elmer

leggi di nuovo la domanda e vedi se ottieni n parti di lunghezza (quasi) uguale con la tua funzione
Muhammad Hasan Khan

@Elmer la tua risposta è giusta, ma la domanda è sbagliata. La tua risposta fornisce un numero sconosciuto di blocchi con dimensioni fisse per ogni blocco (esattamente come Partizione, il nome che hai dato per esso). Ma OP vuole una funzionalità Split in cui fornisce un numero fisso di blocchi con qualsiasi dimensione per blocco (si spera di dimensioni uguali o vicine alla stessa). Forse più adatto qui stackoverflow.com/questions/3773403/...
Nawfal

Penso che puoi semplicemente cambiare i.Index / partitionSize in i.Index% partitionSize e ottenere il risultato richiesto. Lo preferisco anche alla risposta accettata in quanto è più compatta e leggibile.
Jake Drew

2

Questo è efficiente in termini di memoria e differisce l'esecuzione il più possibile (per batch) e opera in tempo lineare 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;
        }
    }

2

Ci sono molte ottime risposte a questa domanda (e ai suoi cugini). Ne avevo bisogno io stesso e avevo creato una soluzione progettata per essere efficiente e tollerante agli errori in uno scenario in cui la raccolta di origine può essere trattata come un elenco. Non utilizza alcuna iterazione lenta, quindi potrebbe non essere adatto per raccolte di dimensioni sconosciute che potrebbero applicare pressione della 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));
    }
}

Ho visto alcune risposte in questa famiglia di domande che utilizzano GetRange e Math.Min. Ma credo che nel complesso questa sia una soluzione più completa in termini di controllo degli errori ed efficienza.


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

1

Ottime risposte, per il mio scenario ho testato la risposta accettata e sembra che non mantenga l'ordine. c'è anche un'ottima risposta di Nawfal che mantiene l'ordine. Ma nel mio scenario volevo dividere il resto in modo normalizzato, tutte le risposte che vedevo si estendevano per il resto o all'inizio o alla fine.

La mia risposta prende anche la diffusione del resto in modo più normalizzato.

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

}

0

Se l'ordine in queste parti non è molto importante puoi provare questo:

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;

Tuttavia questi non possono essere trasmessi a IEnumerable <IEnumerable <int>> per qualche motivo ...


Si può fare. Invece di eseguire il casting diretto, rendi la funzione generica e quindi chiamala per il tuo array int
nawfal

0

Questo è il mio codice, carino e breve.

 <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

0

Questo è il mio modo di elencare gli elementi e suddividere riga per colonna

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

0

Stavo cercando una divisione come quella con stringa, quindi l'intera Lista è divisa secondo una regola, non solo la prima parte, questa è la mia soluzione

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

La prossima volta prova: var nrs = Enumerable.Range (1,2000) .ToList ();
MBoros

0

Ecco una piccola modifica per il numero di elementi invece del numero di parti:

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

-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();

-1

Mi sono appena imbattuto in questo thread e la maggior parte delle soluzioni qui coinvolge l'aggiunta di elementi alle collezioni, materializzando efficacemente ogni pagina prima di restituirla. Questo è negativo per due motivi: in primo luogo se le tue pagine sono grandi, c'è un sovraccarico di memoria per riempire la pagina, in secondo luogo ci sono iteratori che invalidano i record precedenti quando passi a quello successivo (ad esempio se inserisci un DataReader all'interno di un metodo di enumerazione) .

Questa soluzione utilizza due metodi enumeratori annidati per evitare qualsiasi necessità di memorizzare nella cache gli elementi in raccolte temporanee. Poiché gli iteratori esterno e interno attraversano lo stesso enumerabile, condividono necessariamente lo stesso enumeratore, quindi è importante non far avanzare quello esterno finché non si è terminata l'elaborazione della pagina corrente. Detto questo, se decidi di non iterare completamente nella pagina corrente, quando passi alla pagina successiva questa soluzione itererà automaticamente in avanti fino al confine della pagina.

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

Questo non funziona affatto! Il migliore possibile è qui stackoverflow.com/questions/13709626/… ! Vedi i commenti.
SalientBrain
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.