11

I have a collection uni-dimensional like this:

[1,2,4,5.....n]

I would like to convert that collection in a bi-dimensional collection like this:

[[1,2,3],
[4,5,6],
...]

Basically I want to group or split if you want, the array in groups of 'n' members

I can do it with a foreach statement, but I am currently learning LINQ so instead of iterating through all elements and create a new array manually I would like to use the LINQ features (if applicable)

Is there any LINQ function to help me to accomplish this??

I was thinking in the GroupBy or SelectMany I do not know if they will help me though but they might

Any help will be truly appreciate it =) :**

2
  • 1
    It would be possible to write such a query in pure LINQ in many different ways, but I wouldn't recommend doing that, it would be terribly inefficient. Commented May 31, 2012 at 3:21
  • Does this answer your question? Split List into Sublists with LINQ Commented Dec 6, 2022 at 0:21

8 Answers 8

22

You can group by the index divided by the batch size, like this:

var batchSize = 3;
var batched = orig
    .Select((Value, Index) => new {Value, Index})
    .GroupBy(p => p.Index/batchSize)
    .Select(g => g.Select(p => p.Value).ToList());
Sign up to request clarification or add additional context in comments.

1 Comment

I was thinking about how to do this with GroupBy, and couldn't figure how to consider the index... thanks.
5

Use MoreLinq.Batch

 var result = inputArray.Batch(n); // n -> batch size

Example

    var inputs = Enumerable.Range(1,10);

    var output = inputs.Batch(3);


    var outputAsArray = inputs.Batch(3).Select(x=>x.ToArray()).ToArray(); //If require as array

3 Comments

+1 for the link to MoreLinq, thanks! I just want to notice that in some cases we have to embed MoreLinq as a source code instead of compiled dll, e.g. if we use LinqBridge.
This is interesting but i checked the source code and it is done with a foreach
Most of the methods use foreach with careful use of yield keyword. For any such requirement i first try to find out existing extensions in MoreLinq, LinqExtensions, etc. If i dont find one, and it make sense, i create my own extension.
3

You want Take() and Skip(). These methods will let you split an IEnumerable. Then you can use Concat() to slap them together again.

Comments

2

The sample below will split an array into groups of 4 items each.

int[] items = Enumerable.Range(1, 20).ToArray(); // Generate a test array to split
int[][] groupedItems = items
                         .Select((item, index) => index % 4 == 0 ? items.Skip(index).Take(4).ToArray() : null)
                         .Where(group => group != null)
                         .ToArray();

Comments

2

It's not a pure LINQ but it's intended to be used with it:

public static class MyEnumerableExtensions
{
    public static IEnumerable<T[]> Split<T>(this IEnumerable<T> source, int size)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source can't be null.");
        }

        if (size == 0)
        {
            throw new ArgumentOutOfRangeException("Chunk size can't be 0.");
        }

        List<T> result = new List<T>(size);
        foreach (T x in source)
        {
            result.Add(x);
            if (result.Count == size)
            {
                yield return result.ToArray();
                result = new List<T>(size);
            }
        }
    }
}

It can be used from your code as:

private void Test()
{
    // Here's your original sequence
    IEnumerable<int> seq = new[] { 1, 2, 3, 4, 5, 6 };

    // Here's the result of splitting into chunks of some length 
    // (here's the chunks length equals 3). 
    // You can manipulate with this sequence further, 
    // like filtering or joining e.t.c.
    var splitted = seq.Split(3);
}

4 Comments

A semantic point: "splice" in English actually means "join together", whereas the method is actually pulling the array apart.
on last step if result.Count < size, last part of source will be lost
@gabba yeah, thanks! I assumed that the initial sequence would consist of k*size elements. I think it would be better to throw an exception if this is not true.
@DmitryLobanov, When in doubt, use "cleave" -- it means both "to split apart" and "to fuse together!" As the saying goes, "English doesn't borrow from other languages. English follows other languages down dark alleys, knocks them over, and rummages through their pockets for loose grammar."
1

It's as simple as:

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> ToPages<T>(this IEnumerable<T> elements, int pageSize)
    {
        if (elements == null)
            throw new ArgumentNullException("elements");
        if (pageSize <= 0)
            throw new ArgumentOutOfRangeException("pageSize","Must be greater than 0!");

        int i = 0;
        var paged = elements.GroupBy(p => i++ / pageSize);
        return paged;
    }
}

Comments

1

Starting with .NET 6, there is the System.Linq.Enumerable.Chunk(this IEnumerable<TSource>, int size) extension method. It returns an IEnumerable<TSource[]> where each item is an array of size elements, except the last item, which could have fewer.

Code like this:

using System;
using System.Collections.Generic;
using System.Linq;

int[] input = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

IEnumerable<int[]> chunks = input.Chunk(3);

foreach (int[] chunk in chunks)
{
    foreach (int i in chunk)
    {
        Console.Write($"{i} ");
    }
    
    Console.WriteLine();
}

outputs

1 2 3 
4 5 6 
7 8 9 
10 

Comments

0

I based my solution of Jeremy Holovacs's answer and used Take() and Skip() to create subarrays.

        const int batchSize = 3;
        int[] array = new int[] { 1,2,4,5.....n};

        var subArrays = from index in Enumerable.Range(0, array.Length / batchSize + 1)
                      select array.Skip(index * batchSize).Take(batchSize);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.