Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grouping lists into groups of X items per group

I'm having a problem knowing the best way to make a method to group a list of items into groups of (for example) no more than 3 items. I've created the method below, but without doing a ToList on the group before I return it, I have a problem with it if the list is enumerated multiple times.

The first time it's enumerated is correct, but any additional enumeration is thrown off because the two variables (i and groupKey) appear to be remembered between the iterations.

So the questions are:

  • Is there a better way to do what I'm trying to achieve?
  • Is simply ToListing the resulting group before it leaves this method really such a bad idea?

    public static IEnumerable<IGrouping<int, TSource>> GroupBy<TSource>
                  (this IEnumerable<TSource> source, int itemsPerGroup)
    {
        const int initial = 1;
        int i = initial;
        int groupKey = 0;
    
        var groups = source.GroupBy(x =>
        {
            if (i == initial)
            {
                groupKey = 0;
            }
    
            if (i > initial)
            {
                //Increase the group key if we've counted past the items per group
                if (itemsPerGroup == initial || i % itemsPerGroup == 1)
                {
                    groupKey++;
                }
            }
    
            i++;
    
            return groupKey;
        });
    
        return groups;
    }
    
like image 481
ajbrun Avatar asked May 28 '14 20:05

ajbrun


People also ask

How do you Groupby a list in Python?

You can use itertools. groupby() to handle not only lists but also tuples, strings, etc. Use tuple() if you want to make a group a tuple instead of a list. Use join() if you want to make a group into a string.


1 Answers

Here's one way to do this using LINQ...

public static IEnumerable<IGrouping<int, TSource>> GroupBy<TSource>
    (this IEnumerable<TSource> source, int itemsPerGroup)
{
    return source.Zip(Enumerable.Range(0, source.Count()),
                      (s, r) => new { Group = r / itemsPerGroup, Item = s })
                 .GroupBy(i => i.Group, g => g.Item)
                 .ToList();
}

Live Demo

like image 141
Anthony Chu Avatar answered Sep 22 '22 01:09

Anthony Chu