LINQ ile bir koleksiyon "n" parçalarına bölünsün mü?


122

nLINQ ile bir koleksiyonu parçalara ayırmanın güzel bir yolu var mı ? Tabii ki eşit olarak değil.

Yani, koleksiyonu alt koleksiyonlara bölmek istiyorum, her biri son koleksiyonun parçalanabileceği öğelerin bir alt kümesini içerir.


1
Yeniden etiketlendi: Sorunun asp.net ile ilgisi yok. Lütfen sorularınızı uygun şekilde etiketleyin.

Öyle olmasa bile, tam olarak nasıl bölünmelerini istiyorsunuz (tabii ki sonuna kadar göz önünde bulundurarak)?
Marc Gravell

1
bu soruya kim bağlandı? john sen miydin? :-) aniden bütün bu cevaplar :-)
Simon_Weaver


@Simon_Weaver Kabul edilen cevaba göre ne sorduğunuzu açıklamaya çalıştım. Aslında, bir listeyi 'bölmenin', listenin her bir öğesini kendi öğelerine ayırmak ve bunları 'paralel' olarak adlandırılan listelere koymak da dahil olmak üzere birçok yolu vardır.
jpaugh

Yanıtlar:


127

Saf bir bağlantı ve en basit çözüm aşağıda gösterildiği gibidir.

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
Şunları yapabilirsiniz: (IEnumerable <T>) bölümünü seçmek yerine part.AsEnumerable () öğesini seçin. Daha zarif hissettiriyor.
tuinstoel

2
Tüm bu modül işlemlerini yapmak uzun listelerde biraz pahalı olabilir.
Jonathan Allen

8
Dizini içeren Select aşırı yüklemesini kullanmak daha iyi olacaktır.
Marc Gravell

1
Seçili aşırı yük ve yöntem zincirleme sözdizimini kullanan bir yanıt ekledim
reustmd

1
.AsEnumerable()gerekli değil, IGrouping <T> zaten bir IEnumerable <T>.
Alex

58

DÜZENLEME: Tamam, soruyu yanlış anladım gibi görünüyor. Bunu "n adet" yerine "n adet uzunluğunda parçalar" olarak okudum. Doh! Cevabı silme düşünülüyor ...

(Orijinal cevap)

LINQ to Objects'e eklemelerimde bir tane yazmak niyetinde olsam da, yerleşik bir bölümleme yolu olduğuna inanmıyorum. Marc Gravell'in burada bir uygulaması var, ancak muhtemelen salt okunur bir görünüm döndürmek için değiştirecek olsam da:

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

Kahretsin - beni yen ;-p
Marc Gravell

3
Bu "dizi [sayım ++]" ı gerçekten sevmiyorsunuz, eh ;-p
Marc Gravell

18
OP için bir cevap olmamasına rağmen silmediğiniz için teşekkür ederim, ben de aynı şeyi istedim - uzunluk parçaları n :).
Gishu

2
@Dejan: Hayır değil. Kullanımına dikkat edin yield return. Biri gerektiren toplu bir anda bellekte olması, ama hepsi bu.
Jon Skeet

1
@Dejan: Sağ - Kalmaz gibi :) dürüst olmak gerekirse, Paralel LINQ bölümleme etkileşimleri hakkında tahmin etmeye
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
SQL tarzı Linq'ten mantıksız bir hoşlanmam var, bu yüzden bu benim favori cevabım.
piedar

1
@ manu08, ur kodunu denedim, bir listem var var dept = {1,2,3,4,5}. Bölündükten sonra sonuç nasıl dept1 = {1,3,5}ve dept2 = { 2,4 }nerede olur parts = 2. Ama ihtiyaç i sonucudur dept1 = {1,2,3}vedept2 = {4,5}
Karthik Arthik

3
Modulo ile aynı sorunu yaşadım, bu yüzden sütun uzunluğunu hesapladım ve int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);sonra ile bölme yaptım .GroupBy(x => x.index / columnLength). Bir dezavantajı Count () listeyi numaralandırmasıdır.
goodeye

24

Tamam, ringe şapkamı atacağım. Algoritmamın avantajları:

  1. Pahalı çarpma, bölme veya modül operatörleri yok
  2. Tüm işlemler O (1) 'dir (aşağıdaki nota bakın)
  3. IEnumerable <> kaynağı için çalışır (Count özelliği gerekmez)
  4. Basit

Kod:

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

Aşağıdaki yorumlarda belirtildiği gibi, bu yaklaşım aslında yaklaşık olarak eşit uzunlukta sabit sayıda bölüm isteyen orijinal soruyu ele almamaktadır. Bununla birlikte, şu şekilde adlandırarak orijinal soruyu çözmek için yaklaşımımı kullanmaya devam edebilirsiniz:

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

Bu şekilde kullanıldığında, Count () işlemi O (N) olduğundan yaklaşım artık O (1) değildir.


Harika - burada en iyi çözüm! Birkaç optimizasyon: * Her bölüm için yeni bir tane oluşturmak yerine bağlantılı listeyi temizleyin. Bağlantılı listeye yapılan bir referans asla arayana geri dönmez, bu nedenle tamamen güvenlidir. * İlk öğeye ulaşana kadar bağlantılı listeyi oluşturmayın - bu şekilde kaynak boşsa ayırma olmaz
ShadowChaser

3
@ShadowChaser MSDN'ye göre LinkedList'in temizlenmesi O (N) karmaşıklıktır, bu yüzden O (1) hedefimi mahveder. Tabii ki, foreach'in O (N) olduğunu iddia edebilirsiniz ... :)
Mike

4
cevabınız doğru, ancak soru bunun için yanlış. Cevabınız, her bir parça için sabit boyutlu bilinmeyen sayıda parça veriyor. Ancak OP, yığın başına herhangi bir boyuta sahip sabit sayıda parça (umarız eşit veya eşit boyutlara yakın) veren bir Bölme işlevi ister. Belki daha uygun olan burada stackoverflow.com/questions/3773403/…
nawfal

1
@Mike kıyasladın mı? Umarım O (1) 'in daha hızlı anlamına gelmediğini biliyorsunuzdur, bu sadece bölümleme için gereken sürenin ölçeklenmediği anlamına gelir. Tüm gerçek hayat senaryoları için diğer O (n) 'lerden daha yavaş olabildiği zaman O (1)' e körü körüne bağlı kalmanın mantığının ne olduğunu merak ediyorum. Hatta çılgın bir 10 ^ 8 güç listesi için test ettim ve benimki daha da hızlı görünüyordu. Umarım 10 ^ 12 öğe alabilen standart koleksiyon türleri bile olmadığını biliyorsunuzdur ..
nawfal

1
@nawfal - Ayrıntılı analiziniz için teşekkür ederim, ayak parmaklarımda kalmamı sağlıyor. Bağlantılı listeler genel olarak verimli uç eklemeleriyle bilinir, bu yüzden burada seçtim. Bununla birlikte, sadece karşılaştırmalı olarak değerlendirdim ve aslında Liste <> çok daha hızlı. Bunun bir çeşit .NET uygulama detayı olduğundan şüpheleniyorum, belki de ayrı bir StackOverflow sorusunu hak ediyor. Cevabımı, önerinize göre Liste <> kullanmak için değiştirdim. Liste kapasitesinin önceden tahsis edilmesi, son eklemenin hala O (1) olduğunu ve orijinal tasarım hedefimi karşıladığını garanti ediyor. Ayrıca .NET 4.5'te yerleşik .AsReadOnly () sürümüne geçtim.
Mike

16

Bu, kabul edilen cevapla aynı, ancak çok daha basit bir temsil:

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

Yukarıdaki yöntem IEnumerable<T>, eşit büyüklükte veya eşit boyutlara yakın N sayıda parçaya böler .

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

Yukarıdaki yöntem, bir IEnumerable<T> istenen sabit boyutta parçalara ve toplam parça sayısı önemsizdir - bu soru bununla ilgili değildir.

İle ilgili sorun SplitYöntemle , daha yavaş olmasının yanı sıra, çıktıyı, gruplamanın her bir pozisyon için i'inci N katı temelinde yapılacağı veya başka bir deyişle parçaları alamayacağınız anlamında karıştırmasıdır. orijinal sırayla.

Buradaki hemen hemen her yanıt ya düzeni korumaz ya da bölme ve bölme değil ya da açıkça yanlıştır. Daha hızlı olan, düzeni koruyan ancak biraz daha ayrıntılı olan şunu deneyin:

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

Bir eşdeğer bir yöntem Partitionişlemi burada


6

Daha önce yayınladığım Partition işlevini oldukça sık kullanıyorum. Bununla ilgili tek kötü şey, tamamen akış olmamasıydı. Dizinizde birkaç öğeyle çalışırsanız bu bir sorun değildir. Sıralamada 100.000'den fazla elemanla çalışmaya başladığımda yeni bir çözüme ihtiyacım vardı.

Aşağıdaki çözüm çok daha karmaşıktır (ve daha fazla koddur!), Ancak çok verimlidir.

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

    }
}

Zevk almak!


Bu sürüm, IEnumerator sözleşmesini bozar. Reset çağrıldığında InvalidOperationException oluşturmak geçerli değil - LINQ uzantı yöntemlerinin çoğunun bu davranışa dayandığına inanıyorum.
ShadowChaser

1
@ShadowChaser Bence Reset () bir NotSupportedException oluşturmalı ve her şey yoluna girecek. MSDN belgelerinden: "Sıfırlama yöntemi, COM birlikte çalışabilirliği için sağlanmıştır. Uygulanması zorunlu değildir; bunun yerine, uygulayıcı yalnızca bir NotSupportedException oluşturabilir."
2013

@toong Wow, haklısın. Bunca zamandan sonra bunu nasıl özlediğimden emin değilim.
ShadowChaser

Buggy! Tam olarak hatırlamıyorum, ancak (hatırladığım kadarıyla) istenmeyen bir adım atıyor ve çirkin yan etkilere yol açabilir (örn. Datareader ile). En iyi çözüm burada (Jeppe Stig Nielsen): stackoverflow.com/questions/13709626/…
SalientBrain

4

İlginç konu. Split / Partition'ın bir akış sürümünü elde etmek için numaralayıcılar kullanılabilir ve uzantı yöntemlerini kullanarak numaralandırıcıdan elde edilen diziler kullanılabilir. Verim kullanarak zorunlu kodu işlevsel koda dönüştürmek gerçekten çok güçlü bir tekniktir.

İlk olarak, bir dizi öğeyi tembel bir sıraya dönüştüren bir numaralandırıcı uzantısı:

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

Ve sonra bir diziyi bölümleyen numaralandırılabilir bir uzantı:

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

Sonuç, çok basit bir koda dayanan son derece verimli, akışlı ve tembel bir uygulamadır.

Zevk almak!


Başlangıçta aynı şeyi programladım, ancak iç içe yerleştirilmiş IEnumerable <T> örneklerinden birinde Reset çağrıldığında desen bozuluyor.
ShadowChaser

1
İç numaralandırılabilir değil, yalnızca bölümü numaralandırırsanız, bu yine de çalışır mı? iç numaralandırıcı ertelendiğinden, numaralandırılıncaya kadar iç (akımdan alma) kodunun hiçbiri çalışmayacaktır, bu nedenle movenext () sadece dış bölüm işlevi tarafından çağrılacaktır, değil mi? Varsayımlarım doğruysa, bu potansiyel olarak orijinal numaralandırılabilirde n öğeli n bölüm verebilir ve iç numaralandırılabilirler beklenmedik sonuçlar doğurur
Brad

@Brad, beklediğiniz gibi "başarısız" olacak, bu iş parçacığındaki bazı sorunlar stackoverflow.com/questions/419019/… (özellikle stackoverflow.com/a/20953521/1037948 )
drzaus

4

Bunu kullanıyorum:

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

Lütfen nedenini açıklayın. Bu işlevi sorunsuz bir şekilde kullanıyorum!
Elmer

soruyu tekrar okuyun ve işlevinizle n (neredeyse) eşit uzunlukta parça alıp almadığınızı görün
Muhammad Hasan Khan

@Elmer cevabınız doğru, ancak soru bunun için yanlış. Cevabınız, her bir parça için sabit boyutlu bilinmeyen sayıda parça verir (tam olarak Bölüm olarak, bunun için verdiğiniz ad). Ancak OP, yığın başına herhangi bir boyuta sahip sabit sayıda parça (umarız eşit veya eşit boyutlara yakın) veren bir Bölme işlevi ister. Belki daha uygun olan burada stackoverflow.com/questions/3773403/…
nawfal

Sanırım i.Index / partitionSize'ı i.Index% partitionSize olarak değiştirebilir ve istenen sonucu elde edebilirsiniz. Ayrıca, daha derli toplu ve okunabilir olduğu için bunu kabul edilen cevaba tercih ederim.
Jake Drew

2

Bu bellek verimlidir ve yürütmeyi olabildiğince geciktirir (parti başına) ve doğrusal O (n) zamanında çalışır

    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

Bu sorunun (ve kuzenlerinin) birçok harika cevabı var. Buna kendim ihtiyacım vardı ve kaynak koleksiyonunun bir liste olarak değerlendirilebileceği bir senaryoda verimli ve hataya toleranslı olacak şekilde tasarlanmış bir çözüm yaratmıştım. Herhangi bir tembel yineleme kullanmaz, bu nedenle bellek baskısı uygulayabilecek bilinmeyen boyuttaki koleksiyonlar için uygun olmayabilir.

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

GetRange ve Math.Min kullanan bu soru ailesinde birkaç yanıt gördüm. Ancak genel olarak bunun hata kontrolü ve verimlilik açısından daha eksiksiz bir çözüm olduğuna inanıyorum.


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

Harika Cevaplar, senaryom için kabul edilen cevabı test ettim ve görünüşe göre düzeni sağlamıyor. Nawfal'ın da düzeni sağlayan müthiş bir cevabı var. Ama senaryomda kalanı normalleştirilmiş bir şekilde bölmek istedim, gördüğüm tüm cevaplar geri kalanı ya da başında ya da sonunda yaydı.

Cevabım ayrıca geri kalanın daha normal bir şekilde yayılmasını da alıyor.

 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

Bu bölümlerdeki sipariş çok önemli değilse, şunu deneyebilirsiniz:

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;

Ancak bunlar bazı nedenlerle IEnumerable <IEnumerable <int>> 'e dönüştürülemez ...


Yapılabilir. Doğrudan çevrim yerine işlevi genel yapın ve ardından onu int diziniz için çağırın
nawfal

0

Bu benim kodum, güzel ve kısa.

 <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

Bu benim yöntemim, öğeleri listeleme ve satırları sütunlara ayırma

  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

İpli gibi bir bölme arıyordum, bu nedenle tüm Liste bazı kurallara göre bölünmüş, sadece ilk bölüm değil, bu benim çözümüm

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

Bir dahaki sefere şunu deneyin: var nrs = Enumerable.Range (1,2000) .ToList ();
MBoros

0

İşte parça sayısı yerine parça sayısı için küçük bir ayar:

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

Bu konuya yeni rastladım ve buradaki çözümlerin çoğu, koleksiyonlara öğeler eklemeyi, her sayfayı geri vermeden önce etkili bir şekilde gerçekleştirmeyi içeriyor. Bu, iki nedenden dolayı kötüdür - birincisi, sayfalarınız büyükse, sayfayı doldurmak için bir bellek ek yükü vardır, ikinci olarak, bir sonrakine geçtiğinizde önceki kayıtları geçersiz kılan yineleyiciler vardır (örneğin, bir Veri Okuyucuyu bir numaralandırıcı yöntemine sararsanız) .

Bu çözüm, öğeleri geçici koleksiyonlarda önbelleğe alma gereksinimini ortadan kaldırmak için iki iç içe yerleştirilmiş numaralandırıcı yöntemi kullanır. Dış ve iç yineleyiciler aynı numaralandırıcıyı geçtiği için, zorunlu olarak aynı numaralandırıcıyı paylaşırlar, bu nedenle, geçerli sayfayı işlemeyi bitirene kadar dıştaki olanı ilerletmemek önemlidir. Bununla birlikte, mevcut sayfanın tamamını yinelememeye karar verirseniz, sonraki sayfaya geçtiğinizde bu çözüm otomatik olarak sayfa sınırına ileriye doğru yinelenecektir.

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

Bu hiç çalışmıyor! Mümkün olan en iyisi burada stackoverflow.com/questions/13709626/… ! Yorumlara bakınız.
SalientBrain
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.