如何将一个数组分成每组n个元素?

23

如何在C# 4中将数组分组为每个包含n个元素的数组列表,最佳方法是什么。

例如:

string[] testArray = { "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8" };

如果我们取n=3,那么应该分割。

string[] A1 = {"s1", "s2", "s3"};
string[] A2 = {"s4", "s5", "s6"};
string[] A3 = {"s7", "s8"};

可能有一种使用 LINQ 的简单方法吗?

7个回答

26

这将生成一个包含3个元素的字符串数组的数组:

int i = 0;
var query = from s in testArray
            let num = i++
            group s by num / 3 into g
            select g.ToArray();
var results = query.ToArray();

1
+1,这种方法唯一的缺点是它被急切地计算了。在返回单个元素之前必须处理整个查询。 - JaredPar
@JaredPar:观点很好;但是,根据集合的大小或者处理的性质,惰性评估可能被高估了。即便如此,您的解决方案提供了一个有效的惰性方法,+1。 - kbrimington

9
我认为没有伟大的内置方法来实现此功能,但您可以编写以下方法。
public static IEnumerable<IEnumerable<T>> GroupInto<T>(
  this IEnumerable<T> source,
  int count) {

  using ( var e = source.GetEnumerator() ) {
    while ( e.MoveNext() ) { 
      yield return GroupIntoHelper(e, count);
    }
  }    
}

private static IEnumerable<T> GroupIntoHelper<T>(
  IEnumerator<T> e,
  int count) {

  do {
    yield return e.Current;
    count--;
  } while ( count > 0 && e.MoveNext());
}

完美的答案。但应该提供一个解决问题的例子。 - Mare Infinitus

8
int size = 3;
var results = testArray.Select((x, i) => new { Key = i / size, Value = x })
                       .GroupBy(x => x.Key, x => x.Value, (k, g) => g.ToArray())
                       .ToArray();

如果不介意结果以IEnumerable<IEnumerable<T>>的形式呈现而不是T[][],那么您可以完全省略ToArray调用:

int size = 3;
var results = testArray.Select((x, i) => new { Key = i / size, Value = x })
                       .GroupBy(x => x.Key, x => x.Value);

2
如果你处理的是数组而不是一般的IEnumerables,特别是如果这些数组非常大,那么这种方法是非常快速和内存高效的。如果你只是想要一个LINQ语句,那就算了。
    private static T[][] SliceArray<T>(T[] source, int maxResultElements)
    {
        int numberOfArrays = source.Length / maxResultElements;
        if (maxResultElements * numberOfArrays < source.Length)
            numberOfArrays++;
        T[][] target = new T[numberOfArrays][];
        for (int index = 0; index < numberOfArrays; index++)
        {
            int elementsInThisArray = Math.Min(maxResultElements, source.Length - index * maxResultElements);
            target[index] = new T[elementsInThisArray];
            Array.Copy(source, index * maxResultElements, target[index], 0, elementsInThisArray);
        }
        return target;
    }

2

1
感谢您为Stack Overflow社区做出的贡献。这可能是一个正确的答案,但如果您能提供代码的额外解释,让开发人员能够理解您的思路,那将非常有帮助。对于那些对语法不太熟悉或者正在努力理解概念的新手开发人员来说,这尤其有用。您是否可以友好地编辑您的答案,以便为社区的利益提供更多细节? - undefined

1
你可以使用这个扩展。
public static class Extension
{
    private static IEnumerable<TList> Split<TList, T>(this TList value, int countOfEachPart) where TList : IEnumerable<T>
    {
        int cnt = value.Count() / countOfEachPart;
        List<IEnumerable<T>> result = new List<IEnumerable<T>>();
        for (int i = 0; i <= cnt; i++)
        {
            IEnumerable<T> newPart = value.Skip(i * countOfEachPart).Take(countOfEachPart).ToArray();
            if (newPart.Any())
                result.Add(newPart);
            else
                break;
        }

        return result.Cast<TList>();
    }

    public static IEnumerable<IDictionary<TKey, TValue>> Split<TKey, TValue>(this IDictionary<TKey, TValue> value, int countOfEachPart)
    {
        IEnumerable<Dictionary<TKey, TValue>> result = value.ToArray()
                                                            .Split(countOfEachPart)
                                                            .Select(p => p.ToDictionary(k => k.Key, v => v.Value));
        return result;
    }

    public static IEnumerable<IList<T>> Split<T>(this IList<T> value, int countOfEachPart)
    {
        return value.Split<IList<T>, T>(countOfEachPart);
    }

    public static IEnumerable<T[]> Split<T>(this T[] value, int countOfEachPart)
    {
        return value.Split<T[], T>(countOfEachPart);
    }

    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> value, int countOfEachPart)
    {
        return value.Split<IEnumerable<T>, T>(countOfEachPart);
    }
}

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接