LINQでコレクションを「n」個の部分に分割しますか?


122

nLINQでコレクションをパーツに分割する良い方法はありますか?もちろん、必ずしも均等ではありません。

つまり、コレクションをサブコレクションに分割します。サブコレクションにはそれぞれ要素のサブセットが含まれており、最後のコレクションを不規則にすることができます。


1
再タグ付け:質問はasp.netとは関係ありません。質問には適切にタグを付けてください。

均等でなくても、どれだけ正確に分割したいですか(もちろん、最後まで考慮して)?
Marc Gravell

1
この質問にリンクしたのは誰ですか?ジョンはあなたでしたか?:-)突然これらすべての答え:-)
Simon_Weaver 2009年


@Simon_Weaver受け入れられた回答に基づいて、あなたが何を求めているのかを明確にしようとしました。実際、リストの各要素をその要素に分解し、それらをいわゆる「並列」リストに入れるなど、リストを「分割」するには多くの方法があります。
jpaugh

回答:


127

純粋なlinqと最も簡単なソリューションは以下のとおりです。

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
次のことができます:select(IEnumerable <T>)partの代わりにpart.AsEnumerable()を選択します。よりエレガントに感じます。
tuinstoel 2009

2
これらのすべてのモジュラス演算を実行すると、長いリストでは少しコストがかかる可能性があります。
ジョナサンアレン

8
インデックスを含むSelectオーバーロードを使用することをお勧めします。
Marc Gravell

1
selectオーバーロードとメソッドチェーン構文を使用する応答を追加しました
reustmd

1
.AsEnumerable()IGrouping <T>はすでにIEnumerable <T>です。
Alex

58

編集:さて、私は質問を誤って読んだようです。「n個」ではなく「長さn個」と読みました。どー!回答の削除を検討しています...

(元の答え)

LINQ to Objectsへの追加セットの1つにパーティションを作成するつもりですが、組み込みのパーティション分割方法があるとは思いません。Marc Gravellにはここに実装がありますが、おそらく読み取り専用のビューを返すように変更します。

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

ダーンは- ;-pそれに私を打つ
マルクGravell

3
あなたそれらの "array [count ++]" が本当に好きでありません、ええと;-p
マークグラベル

18
OPに対する回答ではありませんが、削除しないでいただき、ありがとうございます。まったく同じものが必要でした-長さn :)の断片。
Gishu

2
@Dejan:いいえ、ありません。の使用に注意してくださいyield return。一度に1つのバッチをメモリに格納する必要がありますが、それだけです。
Jon Skeet、2014

1
@Dejan:正しい-正直に言うと、それが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));
    }
}

28
SQLスタイルのLinqが嫌いなので、これが私のお気に入りの答えです。
ピエダー2014年

1
@ manu08、私はあなたのコードを試しました、私はリストを持っていますvar dept = {1,2,3,4,5}。分割後の結果は次のようであるdept1 = {1,3,5}dept2 = { 2,4 }どこparts = 2。しかし、私が必要とする結果はdept1 = {1,2,3}、そしてdept2 = {4,5}
Karthik Arthik

3
moduloでも同じ問題が発生したため、列の長さを計算し、でint columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);除算しました.GroupBy(x => x.index / columnLength)。欠点の1つは、Count()がリストを列挙することです。
goodeye 2017

24

はい、帽子をリングに投げます。私のアルゴリズムの利点:

  1. 高価な乗算、除算、またはモジュラス演算子はありません
  2. すべての操作はO(1)です(下の注を参照)
  3. IEnumerable <>ソースで機能(Countプロパティは不要)
  4. シンプルな

コード:

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

以下のコメントで指摘されているように、このアプローチは、ほぼ同じ長さの固定数のセクションを要求する元の質問には実際には対応していません。そうは言っても、次のように呼び出すことで、元の質問を解決するために私のアプローチを使用できます。

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

この方法で使用すると、Count()演算がO(N)になるため、アプローチはO(1)ではなくなります。


ブリリアント-ここで最高のソリューション!いくつかの最適化:*セクションごとに新しいリストを作成する代わりに、リンクリストをクリアします。リンクリストへの参照が呼び出し元に返されることはないため、完全に安全です。*最初のアイテムに到達するまでリンクリストを作成しないでください-ソースが空の場合、割り当てはありません
ShadowChaser

3
@ShadowChaser MSDNによれば、LinkedListがO(N)の複雑さであることをクリアすると、私の目標であるO(1)が台無しになるでしょう。もちろん、foreachはまずO(N)であると主張することもできます... :)
Mike

4
あなたの答えは正しいですが、質問は間違っています。あなたの答えは、チャンクごとに固定サイズの未知数のチャンクを与えます。しかしOPは、チャンクごとに任意のサイズの固定数のチャンクを提供するスプリット機能を必要としています(できれば、等しいか等しいサイズに近い)。おそらく、もっとここに適しstackoverflow.com/questions/3773403/...
nawfal

1
@マイクあなたはそれをベンチマークしましたか?O(1)が高速であるという意味ではなく、パーティショニングに必要な時間がスケーリングされないことを意味していることをご存知でしょうか。他のO(n)よりも実際のすべてのシナリオで遅くなる可能性がある場合に、盲目的にO(1)に固執する根拠は何ですか?クレイジーな10 ^ 8ストレングスリストでテストしたところ、まだ速いように見えました。私は..あなたは10 ^ 12項目を保持することができさえ標準のコレクション型が存在しないことを知っている願っています
nawfal

1
@nawfal-あなたの詳細な分析をありがとう、それは私のつま先を保つのに役立ちます。リンクリストは一般的に効率的なエンド挿入で知られているため、ここで選択しました。しかし、私はそれをベンチマークしただけで、確かにList <>ははるかに高速です。これはある種の.NET実装の詳細だと思います。おそらく、別のStackOverflowの質問に値するでしょう。私はあなたの提案に従ってList <>を使用するように私の答えを変更しました。リストの容量を事前に割り当てることで、最後の挿入がまだO(1)であり、元の設計目標を満たしていることが保証されます。また、.NET 4.5の組み込み.AsReadOnly()に切り替えました。
Mike

16

これは受け入れられた答えと同じですが、はるかに単純な表現です:

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

上記の方法IEnumerable<T>は、等しいサイズまたは同じサイズに近いN個のチャンクに分割します。

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

上記の方法IEnumerable<T>では、必要な固定サイズのチャンクに分割し、チャンクの総数は重要ではありません。これは問題ではありません。

このSplit方法の問題は、低速であるだけでなく、グループ化が各位置のiのNの倍数に基づいて行われるという意味で出力をスクランブルすることです。つまり、チャンクを取得できません。元の順序で。

ここでのほとんどすべての答えは、順序を維持しないか、分割についてであり分割ではないか、明らかに間違っています。これを試してみてください。より速く、順序は保持されますが、少し冗長になります。

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

ここでのPartition操作と同等の方法


6

以前に投稿したパーティション機能を頻繁に使用しています。唯一の悪い点は、完全にストリーミングされていないことです。シーケンス内の要素が少ない場合は問題ありません。シーケンスで100.000以上の要素の処理を開始したとき、新しいソリューションが必要でした。

次のソリューションははるかに複雑です(そしてコードが増えます!)が、非常に効率的です。

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

    }
}

楽しい!


このバージョンはIEnumeratorの契約に違反します。Resetが呼び出されたときにInvalidOperationExceptionをスローすることは無効です。多くのLINQ拡張メソッドがこの動作に依存していると思います。
ShadowChaser

1
@ShadowChaser Reset()はNotSupportedExceptionをスローするべきだと思います。MSDNドキュメントから:「ResetメソッドはCOMの相互運用性のために提供されています。必ずしも実装する必要はありません。代わりに、実装者は単にNotSupportedExceptionをスローできます。」
toong

@toongうわー、あなたは正しい。結局どうしてそれを逃したのかわからない。
ShadowChaser 2013年

バギーです!私は正確には覚えていませんが、(覚えている限り)不要な手順を実行し、醜い副作用を引き起こす可能性があります(データリーダーなど)。最善の解決策は、ここ(ジャップスティグニールセン)である:stackoverflow.com/questions/13709626/...
SalientBrain

4

興味深いスレッド。スプリット/パーティションのストリーミングバージョンを取得するには、列挙子を使用し、拡張メソッドを使用して列挙子からシーケンスを生成できます。収量を使用して命令型コードを関数型コードに変換することは、非常に強力な手法です。

最初に、要素の数を遅延シーケンスに変換する列挙子拡張機能:

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

そして、シーケンスを分割する列挙可能な拡張:

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

最終結果は、非常に単純なコードに依存する、非常に効率的なストリーミングおよび遅延の実装です。

楽しい!


最初は同じことをプログラムしましたが、ネストされたIEnumerable <T>インスタンスの1つでResetが呼び出されるとパターンが壊れます。
ShadowChaser

1
これは、パーティションを列挙するだけで内部の列挙可能ではない場合でも機能しますか?内部列挙子が据え置かれているので、内部(currentから取得)のコードは、列挙されるまで実行されないため、movenext()は外部パーティション関数によってのみ呼び出されますよね?私の仮定が真である場合、これは潜在的に元の列挙型のn個の要素を持つn個のパーティションを生成する可能性があり、内部列挙型は予期しない結果を生成します
Brad

それはあなたが、このスレッドでの問題のいくつかに似期待として「失敗」します@Brad stackoverflow.com/questions/419019/...(特にstackoverflow.com/a/20953521/1037948
drzaus

4

私はこれを使います:

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

もう一度質問を読んで、関数でn(ほぼ)等しい長さの部分が得られるかどうかを確認してください
Muhammad Hasan Khan

@Elmerあなたの答えは正しいですが、質問は間違っています。あなたの答えは、各チャンクの固定サイズのチャンクの数を正確に与えます(正確には、パーティションに指定した名前です)。しかし、OPは分割機能を必要としています。チャンクごとに任意のサイズの固定数のチャンクを提供します(願わくは等しいか等しいサイズに近い)。おそらく、もっとここに適しstackoverflow.com/questions/3773403/...
nawfal

i.Index / partitionSizeをi.Index%partitionSizeに変更して、要求された結果を取得できると思います。これは、よりコンパクトで読みやすいので、これも受け入れられた回答よりも好みます。
Jake Drew、

2

これはメモリ効率が良く、可能な限り(バッチごとに)実行を延期し、線形時間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

この質問(およびそのいとこ)には多くのすばらしい答えがあります。私はこれを自分で必要とし、ソースコレクションをリストとして扱うことができるシナリオで、効率的でエラー耐性があるように設計されたソリューションを作成しました。レイジー反復を使用しないため、メモリプレッシャーを適用する可能性がある不明なサイズのコレクションには適さない場合があります。

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とMath.Minを使用するこの一連の質問全体でいくつかの回答を見てきました。しかし、私は全体として、これはエラーチェックと効率の点でより完全なソリューションであると信じています。


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

Great Answers、私のシナリオでは、受け入れられた回答をテストしましたが、順序が守られていないようです。秩序を守るナウファルの素晴らしい答えもあります。しかし、私のシナリオでは、残りを正規化された方法で分割したいと思いました。

私の答えは、残りをより正規化された方法で拡散させることです。

 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

これらのパーツの順序があまり重要でない場合は、次の方法を試すことができます。

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;

ただし、これらは何らかの理由でIEnumerable <IEnumerable <int >>にキャストできません...


できます。直接キャストする代わりに、関数をジェネリックにして、int配列に対して呼び出すだけです
nawfal

0

これは私のコードです。

 <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

これは私のやり方です、アイテムをリストし、列ごとに行を分割します

  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

私は文字列のような分割を探していたので、リスト全体が最初の部分だけでなく、いくつかのルールに従って分割され、これが私の解決策です

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

次に試す:var nrs = Enumerable.Range(1,2000).ToList();
MBoros 2017

0

部品の数ではなく、アイテムの数を少し調整します。

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

このスレッドに出くわしました。ここでのほとんどの解決策は、アイテムをコレクションに追加し、各ページを効果的に具体化してからそれを返すことです。これには2つの理由があります。まず、ページが大きい場合、ページを埋めるためのメモリオーバーヘッドがあり、次に、次のレコードに進むときに前のレコードを無効にする反復子があります(たとえば、列挙子メソッド内でDataReaderをラップする場合)。 。

このソリューションでは、2つのネストされた列挙子メソッドを使用して、アイテムを一時コレクションにキャッシュする必要を回避します。外部イテレーターと内部イテレーターは同じ列挙子をトラバースするため、それらは必ず同じ列挙子を共有するため、現在のページの処理が完了するまで外部イテレーターを進めないことが重要です。つまり、現在のページ全体を反復しないことを決定した場合、次のページに移動すると、このソリューションはページ境界まで自動的に反復します。

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

これはまったく機能しません!最善の方法はここにありますstackoverflow.com/questions/13709626/…!コメントを参照してください。
SalientBrain 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.