views:

89

answers:

4

What is the best way to group an array into a list of array of n elements each in c# 4.

E.g

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

should be split into if we take n=3.

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

May be a simple way using LINQ?

+3  A: 

I don't think there's a great built-in method for this but you could write one like the following.

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
+5  A: 

This will generate an array of string arrays having 3 elements:

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
@Amitabh: Thanks. Copy/paste from VS mistake. Fixed.
kbrimington
+1, The only downside of this approach is that it's eagerly evaluated. The entire query must be processed before a single element can be returned.
JaredPar
Thanks a lot it worked..
Amitabh
@JaredPar: Point well taken; however, depending on the size of the collection, or the nature of the processing, lazy evaluation may be overrated. Even so, +1 to your solution for providing a valid lazy approach.
kbrimington
+1  A: 

If it is actually arrays that you are working with rather than general IEnumerables, and especially if the arrays are very large, then this method is a very fast and memory effecient way to do it. If you really just want a LINQ statement, then nevermind.

    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
+1  A: 
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();

If you don't mind the results being typed as IEnumerable<IEnumerable<T>> rather than T[][] then you can omit the ToArray calls altogether:

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