43

午後、

配列を小さな「チャンク」に分割する必要があります。

私は約 1200 個のアイテムを渡しています。これらを分割して、それぞれ 100 個のアイテムの配列を扱いやすくし、それを処理する必要があります。

誰か提案をお願いできますか?

4

9 に答える 9

77

Array.Copy は 1.1 から存在しており、配列をチャンク化する優れた機能を果たします。

string[] buffer;

for(int i = 0; i < source.Length; i+=100)
{
    buffer = new string[100];
    Array.Copy(source, i, buffer, 0, 100);
    // process array
}

そして、それを拡張するには:

public static class Extensions
{
    public static T[] Slice<T>(this T[] source, int index, int length)
    {       
        T[] slice = new T[length];
        Array.Copy(source, index, slice, 0, length);
        return slice;
    }
}

拡張機能を使用するには:

string[] source = new string[] { 1200 items here };

// get the first 100
string[] slice = source.Slice(0, 100);

更新:ArraySegment<> 元の配列をソースとして使用し、「セグメント」を決定するために Offset および Count プロパティを維持するため、パフォーマンス チェックの必要はありません。残念ながら、セグメントだけを配列として取得する方法はないため、次のようにラッパーを作成した人もいます: ArraySegment - 実際のセグメントを返す C#

ArraySegment<string> segment;

for (int i = 0; i < source.Length; i += 100)
{
    segment = new ArraySegment<string>(source, i, 100);

    // and to loop through the segment
    for (int s = segment.Offset; s < segment.Array.Length; s++)
    {
        Console.WriteLine(segment.Array[s]);
    }
}

Array.Copy と Skip/Take と LINQ のパフォーマンス

テスト方法 (リリース モード):

static void Main(string[] args)
{
    string[] source = new string[1000000];
    for (int i = 0; i < source.Length; i++)
    {
        source[i] = "string " + i.ToString();
    }

    string[] buffer;

    Console.WriteLine("Starting stop watch");

    Stopwatch sw = new Stopwatch();

    for (int n = 0; n < 5; n++)
    {
        sw.Reset();
        sw.Start();
        for (int i = 0; i < source.Length; i += 100)
        {
            buffer = new string[100];
            Array.Copy(source, i, buffer, 0, 100);
        }

        sw.Stop();
        Console.WriteLine("Array.Copy: " + sw.ElapsedMilliseconds.ToString());

        sw.Reset();
        sw.Start();
        for (int i = 0; i < source.Length; i += 100)
        {
            buffer = new string[100];
            buffer = source.Skip(i).Take(100).ToArray();
        }
        sw.Stop();
        Console.WriteLine("Skip/Take: " + sw.ElapsedMilliseconds.ToString());

        sw.Reset();
        sw.Start();
        String[][] chunks = source                            
            .Select((s, i) => new { Value = s, Index = i })                            
            .GroupBy(x => x.Index / 100)                            
            .Select(grp => grp.Select(x => x.Value).ToArray())                            
            .ToArray();
        sw.Stop();
        Console.WriteLine("LINQ: " + sw.ElapsedMilliseconds.ToString());
    }
    Console.ReadLine();
}

結果 (ミリ秒単位):

Array.Copy:    15
Skip/Take:  42464
LINQ:         881

Array.Copy:    21
Skip/Take:  42284
LINQ:         585

Array.Copy:    11
Skip/Take:  43223
LINQ:         760

Array.Copy:     9
Skip/Take:  42842
LINQ:         525

Array.Copy:    24
Skip/Take:  43134
LINQ:         638
于 2012-06-26T13:07:53.527 に答える
45

LINQすべての項目をチャンク サイズでグループ化し、後で新しい配列を作成するために使用できます。

// build sample data with 1200 Strings
string[] items = Enumerable.Range(1, 1200).Select(i => "Item" + i).ToArray();
// split on groups with each 100 items
String[][] chunks = items
                    .Select((s, i) => new { Value = s, Index = i })
                    .GroupBy(x => x.Index / 100)
                    .Select(grp => grp.Select(x => x.Value).ToArray())
                    .ToArray();

for (int i = 0; i < chunks.Length; i++)
{
    foreach (var item in chunks[i])
        Console.WriteLine("chunk:{0} {1}", i, item);
}

新しい配列を作成する必要がないことに注意してください (CPU サイクルとメモリが必要です)。IEnumerable<IEnumerable<String>>2 つの を省略した場合にも、 を使用できますToArrays

実行中のコードは次のとおりです: http://ideone.com/K7Hn2

于 2012-06-26T13:08:44.680 に答える
18

ここで私は別のlinq-solutionを見つけました:

int[] source = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int i = 0;
int chunkSize = 3;
int[][] result = source.GroupBy(s => i++ / chunkSize).Select(g => g.ToArray()).ToArray();

//result = [1,2,3][4,5,6][7,8,9]
于 2015-05-22T07:59:55.383 に答える
11

あなたが使用することができSkip()ますTake()

string[] items = new string[]{ "a", "b", "c"};
string[] chunk = items.Skip(1).Take(1).ToArray();
于 2012-06-26T12:41:10.310 に答える
8
    string[]  amzProductAsins = GetProductAsin();;
    List<string[]> chunks = new List<string[]>();
    for (int i = 0; i < amzProductAsins.Count; i += 100)
    {
        chunks.Add(amzProductAsins.Skip(i).Take(100).ToArray());
    }
于 2012-06-26T12:44:35.457 に答える
3

List.GetRangeを使用できます。

for(var i = 0; i < source.Count; i += chunkSize)
{
    List<string> items = source.GetRange(i, Math.Min(chunkSize, source.Count - i));
}

Array.Copy ほど高速ではありませんが、きれいに見えると思います。

var list = Enumerable.Range(0, 723748).ToList();

var stopwatch = new Stopwatch();

for (int n = 0; n < 5; n++)
{
    stopwatch.Reset();
    stopwatch.Start();
    for(int i = 0; i < list.Count; i += 100)
    {
        List<int> c = list.GetRange(i, Math.Min(100, list.Count - i));
    }
    stopwatch.Stop();
    Console.WriteLine("List<T>.GetRange: " + stopwatch.ElapsedMilliseconds.ToString());

    stopwatch.Reset();
    stopwatch.Start();
    for (int i = 0; i < list.Count; i += 100)
    {
        List<int> c = list.Skip(i).Take(100).ToList();
    }
    stopwatch.Stop();
    Console.WriteLine("Skip/Take: " + stopwatch.ElapsedMilliseconds.ToString());

    stopwatch.Reset();
    stopwatch.Start();
    var test = list.ToArray();
    for (int i = 0; i < list.Count; i += 100)
    {
        int length = Math.Min(100, list.Count - i);
        int[] c = new int[length];
        Array.Copy(test, i, c, 0, length);
    }
    stopwatch.Stop();
    Console.WriteLine("Array.Copy: " + stopwatch.ElapsedMilliseconds.ToString());

    stopwatch.Reset();
    stopwatch.Start();
    List<List<int>> chunks = list
        .Select((s, i) => new { Value = s, Index = i })
        .GroupBy(x => x.Index / 100)
        .Select(grp => grp.Select(x => x.Value).ToList())
        .ToList();
    stopwatch.Stop();
    Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds.ToString());
}

ミリ秒単位の結果:

List<T>.GetRange: 1
Skip/Take: 9820
Array.Copy: 1
LINQ: 161

List<T>.GetRange: 9
Skip/Take: 9237
Array.Copy: 1
LINQ: 148

List<T>.GetRange: 5
Skip/Take: 9470
Array.Copy: 1
LINQ: 186

List<T>.GetRange: 0
Skip/Take: 9498
Array.Copy: 1
LINQ: 110

List<T>.GetRange: 8
Skip/Take: 9717
Array.Copy: 1
LINQ: 148
于 2015-12-10T17:17:06.737 に答える
1

LINQ を使用します。Take() および Skip() 関数を使用できます

于 2012-06-26T12:41:46.637 に答える
1

一般的な再帰的拡張方法:

    public static IEnumerable<IEnumerable<T>> SplitList<T>(this IEnumerable<T> source, int maxPerList)
    {
        var enumerable = source as IList<T> ?? source.ToList();
        if (!enumerable.Any())
        {
            return new List<IEnumerable<T>>();
        }
        return (new List<IEnumerable<T>>() { enumerable.Take(maxPerList) }).Concat(enumerable.Skip(maxPerList).SplitList<T>(maxPerList));
    }
于 2016-01-15T18:38:11.740 に答える
-1

分割する配列があるが、この単純なソリューションで分割に残りがある場合、さまざまな「チャンク」に欠けている要素を均等に共有できます。

int[] arrInput = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
var result = SplitArrey(arrInput, 5);
foreach (var item in result) {
  Console.WriteLine("   {0}", String.Join(" ", item));
}

機能は次のとおりです。

public static List<int[]> SplitArrey(int[] arrInput, int nColumn) {

        List<int[]> result = new List<int[]>(nColumn);
    
        int itemsForColum = arrInput.Length / nColumn;  
        int countSpareElement = arrInput.Length - (itemsForColum * nColumn);    

        // Add and extra space for the spare element
        int[] newColumLenght = new int[nColumn];
        for (int i = 0; i < nColumn; i++)
        {
            int addOne = (i < countSpareElement) ? 1 : 0;
            newColumLenght[i] = itemsForColum + addOne;
            result.Add(new int[itemsForColum + addOne]);
        }

        // Copy the values
        int offset = 0;
        for (int i = 0; i < nColumn; i++)
        {
            int count_items_to_copy = newColumLenght[i];
            Array.Copy(arrInput, offset, result[i], 0, count_items_to_copy);
            offset += newColumLenght[i];
        }
        return result;
    }

結果は次のとおりです。

1 2 3
4 5 6
7 8
9 10
11 12
于 2016-02-28T22:22:34.180 に答える