Jay Sinha
Jay Sinha

Reputation: 423

LINQ method to group collection into subgroups with specified number of elements

Does there exist a LINQ method to group a given collection into subgroups with specified number of elements I mean, something like Scala's grouped method.
e.g. in Scala, List(89, 67, 34, 11, 34).grouped(2) gives List(List(89, 67), List(34, 11), List(34)).

In case such a method doesn't exist, what would be the LINQ way to do it?

Upvotes: 3

Views: 2302

Answers (3)

Ivan Zlatanov
Ivan Zlatanov

Reputation: 5226

Yes, you can. But you can argue if it's very pretty...

  Int64[] aValues = new Int64[] { 1, 2, 3, 4, 5, 6 };
  var result = aValues
          .Select( ( x, y ) => new KeyValuePair<Int64, Int32>( x, y ) )
          .GroupBy( x => x.Value / 2 )
          .Select( x => x.Select( y => y.Key ).ToList() ).ToList();

How it works:

Select x and y from the original collection, where x is the actual value and y is the index of it in the given collection. Then group by integer devision of the index and the desired grouping length ( in this example 2 ).

Grouping by integer devision will round up to the lower - so 0 / 2 = 0, 1 / 2 = 0, etc. which will give us the needed grouping category value. This is what we are grouping against here.

For result select only the values grouped in lists and return them as a collection of lists.

Upvotes: 2

Mark Byers
Mark Byers

Reputation: 838296

You could try the approach shown in this answer to this similar question.

public static class GroupingExtension
{
    public static IEnumerable<IEnumerable<T>> Grouped<T>(
        this IEnumerable<T> input,
        int groupCount)
    {
        if (input == null) throw new ArgumentException("input");
        if (groupCount < 1) throw new ArgumentException("groupCount");

        IEnumerator<T> e = input.GetEnumerator();

        while (true)
        {
            List<T> l = new List<T>();
            for (int n = 0; n < groupCount; ++n)
            {
                if (!e.MoveNext())
                {
                    if (n != 0)
                    {
                        yield return l;
                    }
                    yield break;
                }
                l.Add(e.Current);
            }
            yield return l;
        }
    }
}

Use like this:

List<int> l = new List<int>{89, 67, 34, 11, 34};
foreach (IEnumerable<int> group in l.Grouped(2)) {
    string s = string.Join(", ", group.Select(x => x.ToString()).ToArray());
    Console.WriteLine(s);
}

Result:

89, 67
34, 11
34

Upvotes: 2

spinon
spinon

Reputation: 10847

Here is a website that seems to have some sample code to do what you want: http://www.chinhdo.com/20080515/chunking/

So what you could do is take this method and create an extension method.

Extension method sample:

static class ListExtension
{
    public static List<List<T>> BreakIntoChunks<T>(this List<T> list, int chunkSize)
    {
        if (chunkSize <= 0)
        {
            throw new ArgumentException("chunkSize must be greater than 0.");
        }

        List<List<T>> retVal = new List<List<T>>();

        while (list.Count > 0)
        {
            int count = list.Count > chunkSize ? chunkSize : list.Count;
            retVal.Add(list.GetRange(0, count));
            list.RemoveRange(0, count);
        }

        return retVal;
    }
}

Upvotes: 2

Related Questions