Как разбить массив на группу из n элементов?

17

Каков наилучший способ группировать массив в список массива из n элементов, каждый из которых в c # 4.

например

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?     

задан Amitabh 18.08.2010 в 19:17
источник

6 ответов

23

Это создаст массив строковых массивов, имеющих 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();
    
ответ дан kbrimington 18.08.2010 в 19:21
  • +1. Единственным недостатком этого подхода является то, что он с нетерпением оценивается. Весь запрос должен быть обработан до того, как будет возвращен один элемент. –  JaredPar 18.08.2010 в 19:26
  • @JaredPar: точка хорошо взята; однако, в зависимости от размера коллекции или характера обработки, ленивая оценка может быть переоценена. Тем не менее, +1 к вашему решению для обеспечения действительного ленивого подхода. –  kbrimington 18.08.2010 в 19:30
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());
}
    
ответ дан JaredPar 18.08.2010 в 19:21
  • Идеальный ответ. Но должен привести пример того, как он решает проблему в вопросе. –  Mare Infinitus 30.12.2014 в 14:23
5
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);
    
ответ дан LukeH 19.08.2010 в 01:26
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;
    }
    
ответ дан Jeffrey L Whitledge 18.08.2010 в 19:45
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);
    }
}
    
ответ дан dermanyon 25.08.2015 в 10:16
1

MoreLinq предоставляет метод расширения Batch

    
ответ дан sam 09.06.2015 в 09:45