C# 拆分数组的最佳方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11207526/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 16:58:17  来源:igfitidea点击:

Best way to split an array

c#arrayssplit

提问by thatuxguy

Afternoon, I need to find out what the best way to split an array into smaller "chunks" would be.

下午,我需要找出将数组拆分为更小的“块”的最佳方法是什么。

I am passing over about 1200 items, and need to split these into easier to handle groups of 100, then i need to pass them over to processed.

我传递了大约 1200 个项目,需要将它们分成更容易处理的 100 个组,然后我需要将它们传递给处理。

Could any one please make some suggestions?

任何人都可以提出一些建议吗?

采纳答案by Tim Schmelter

You can use LINQto group all items by the chunk size and create new Arrays afterwards.

您可以使用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);
}

Note that it's not necessary to create new arrays(needs cpu cycles and memory). You could also use the IEnumerable<IEnumerable<String>>when you omit the two ToArrays.

请注意,没有必要创建新数组(需要 CPU 周期和内存)。您也可以在IEnumerable<IEnumerable<String>>省略两个时使用ToArrays

Here's the running code: http://ideone.com/K7Hn2

这是运行代码:http: //ideone.com/K7Hn2

回答by Asif Mushtaq

You can use Skip()and Take()

您可以使用Skip()Take()

string[] items = new string[]{ "a", "b", "c"};
string[] chunk = items.Skip(1).Take(1).ToArray();

回答by fenix2222

Use LINQ, you can use Take() and Skip() functions

使用 LINQ,可以使用 Take() 和 Skip() 函数

回答by Habib

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

回答by Chris Gessler

Array.Copy has been around since 1.1 and does an excellent job of chunking arrays.

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
}

And to make an extension for it:

并对其进行扩展:

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

And to use the extension:

并使用扩展名:

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

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

Update: I think you might be wanting ArraySegment<>No need for performance checks, because it simply uses the original array as its source and maintains an Offset and Count property to determine the 'segment'. Unfortunately, there isn't a way to retrieve JUST the segment as an array, so some folks have written wrappers for it, like here: ArraySegment - Returning the actual segment C#

更新:我认为您可能想要ArraySegment<>不需要性能检查,因为它只是使用原始数组作为其源并维护一个 Offset 和 Count 属性来确定“段”。不幸的是,没有办法只将段作为数组检索,所以有些人为它编写了包装器,如下所示:ArraySegment - Returning the actual Segment 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]);
    }
}


Performance of Array.Copy vs Skip/Take vs LINQ

Array.Copy 与 Skip/Take 与 LINQ 的性能

Test method (in Release mode):

测试方法(在Release模式下):

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


Results (in milliseconds):

结果(以毫秒为单位):

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

回答by fubo

hereI found another linq-solution:

在这里我找到了另一个 linq 解决方案:

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]

回答by Daniel

You can use List.GetRange:

您可以使用List.GetRange

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

Although not at fast as Array.Copy, I think it looks cleaner:

虽然不如 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());
}

Results in milliseconds:

结果以毫秒为单位:

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

回答by Joel

General recursive extension method:

一般递归扩展方法:

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

回答by Francesco

If you have an array to be divided but the division has the rest with this simple solution you can share the elements missing into the various "chunks" equally. In my solution the chunks are linked as a long string but you can easily change that.

如果您有一个要划分的数组,但除法以外的其余部分都可以使用这个简单的解决方案,您可以将缺少的元素平均分配到各个“块”中。在我的解决方案中,块链接为一个长字符串,但您可以轻松更改它。

    public static string[] SplitArrey(string[] ArrInput, int n_column)
    {

        string[] OutPut = new string[n_column];
        int NItem = ArrInput.Length; // total elements
        int ItemsForColum = NItem / n_column; // n elements for each chunk
        int _total = ItemsForColum * n_column; // Count the equal elements
        int MissElement = NItem - _total; // Count missing element

        int[] _Arr = new int[n_column];
        for (int i = 0; i < n_column; i++)
        {
            int AddOne = (i < MissElement) ? 1 : 0;
            _Arr[i] = ItemsForColum + AddOne;
        }

        int offset = 0;
        for (int Row = 0; Row < n_column; Row++)
        {
            for (int i = 0; i < _Arr[Row]; i++)
            {
                OutPut[Row] += ArrInput[i + offset] + " "; // <- Here to change how the strings are linked 
            }
            offset += _Arr[Row];
        }
        return OutPut;
    }