tags:

views:

172

answers:

4

Given an IEnumerable<T> and row count, I would like to convert it to an IEnumerable<IEnumerable<T>> like so:

Input:

Row Count: 3

List: [1,2,3,4,5,6,7]

Output

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

EDIT I would like this to work for any IEnumerable and not depend on T being an Int32.

How can I do this using LINQ?

+1  A: 

This will grab groups of 3 if that's what your after?

Nasty approach though :-)

int[] list = new [] {1,2,3,4,5,6,7};

    int count = 0;

    var x =
        from i in list.Where((c,i)=> i%3==0)
        let grp = list.Skip(count).Take(3)
        let temp = (count+=3)
        select new {
            grp
        };
Sir Psycho
+14  A: 

This will do it:

var list = new List<object> { 1, "two", 3, 4, 5, 6 ,7 };
int count = 3;
var rows = list.Select((item, index) => new { Item = item, Index = index })
            .GroupBy(o => o.Index % count)
            .Select(g => g.Select(o => o.Item));

It should work for any Enumerable

David M
Much nicer approach :)
Sir Psycho
what is the out here?
VoodooChild
@VoodooChild: The ouput type is `IEnumerable<IGrouping<int, int>>`.
Guffa
yerp this does the trick, wonder how efficient it is though, does it run through the list more than once?
Sam Saffron
@VoodooChild: what Guffa said. But the point is that the grouping is itself `IEnumerable<T>`, so it does what you want...
David M
@Sam Saffron - I very much doubt it. These Linq extension methods are pretty well implemented for the most part. I'll take a look in Reflector later and let you know.
David M
Ideally I need this to work for this as well ... Enumerable.Range(1, 10).Select(_ => "a").ToList();
Sam Saffron
OK I amended this answer to something that works on any kind of list. will save on having a dupe answer.
Sam Saffron
wow, didn't know that there is overloaded select with (item, index) delegate... definitely it is the most correct answer.
zerkms
@Sam: Actually, this doesn't run through the list at all. It creates an expression that can return the result, so when you loop over the result it will run through the list once. If you use the result more than once, it will read the list each time, so you can add `.ToList()` at the end to realise the result into a `List<IGrouping<int, int>>` that you can reuse.
Guffa
@Guffa, sure, fortunately my problem only requires a single run through the list, I agree a few ToLists sprinkled strategically could help performance. Also I think its quite possible that hand coding this could be faster.
Sam Saffron
@Sam: Yes, using extension methods adds a small overhead, so it's always possible to make it slightly faster "by hand". On the other hand, the extension methods are well written and using them gives simple code, so it's usually not worth it to try to improve on them.
Guffa
A: 

Something like this should work.

int[] list = new[] {1,2,3,4,5,6,7};
int[] offsets = new[] {0,1,2};
int[][] sorted = 
    offsets.Select((offset) => list.TakeWhile((_,ix,_) => ix % 3 == offset))

Warning: not tested. I don't program in c#, but the idea should be clear.

Marc
+2  A: 
var src = new List<int> { 2, 4, 8, 16, 23, 42, 101 };
var i = 0;
var answer = src.GroupBy(item => i++ % 3)
                .Select(item => item.ToArray())
                .ToArray();

hehe, and with this trick we can avoid temporary variable, but code become unreadable:

        var src = new int[] { 4, 8, 15, 16, 23, 42, 101 };

        var b = Enumerable.Range(0, src.Length)
                   .Select(index => index)
                   .GroupBy(index => index % 3)
                   .Select(indexes => indexes.Select(index => src[index]).ToArray())
                   .ToArray();
zerkms