Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ - GroupBy a key and then put each grouped item into separate 'buckets'

I have a list of items as such:

public class Item
{
    public int ItemId { get; set; }
    public string ItemName { get; set; }
    public int ListId { get; set; }
}

1 Test1 1

2 Test2 1

3 Test3 1

4 List 2

5 List2 2

6 Testing 3

7 Testing2 3

8 Testing3 3

Is there a way for me to group by the ListId and put them into each separate buckets, i.e, ListId1 bucket will have all items with ListId == 1. The list is dynamically returned from SQL, so I don't know before hand how many ListId there will be.

like image 230
Saxman Avatar asked Jul 08 '11 00:07

Saxman


2 Answers

You can use GroupBy:

var groups = items.GroupBy(item => item.ListId);

foreach(var group in groups)
{
     Console.WriteLine("List with ID == {0}", group.Key);
     foreach(var item in group)
        Console.WriteLine("    Item: {0}", item.ItemName);
}
like image 164
Reed Copsey Avatar answered Nov 09 '22 23:11

Reed Copsey


Let's create your list of items:

List<Item> items = new List<Item>();
items.Add(new Item() { ItemId = 1, ItemName = "Test1", ListId = 1 });
items.Add(new Item() { ItemId = 2, ItemName = "Test2", ListId = 1 });
items.Add(new Item() { ItemId = 3, ItemName = "Test3", ListId = 1 });
items.Add(new Item() { ItemId = 4, ItemName = "List", ListId = 2 });
items.Add(new Item() { ItemId = 5, ItemName = "List2", ListId = 2 });
items.Add(new Item() { ItemId = 6, ItemName = "Testing", ListId = 3 });
items.Add(new Item() { ItemId = 7, ItemName = "Testing2", ListId = 3 });
items.Add(new Item() { ItemId = 8, ItemName = "Testing3", ListId = 3 });

var groupByResult = items.GroupBy(i => i.ListId);

After this GroupBy call, groupByResult is a variable of type IEnumerable<IGrouping<int, Item>> which is basically a collection of objects that implement IGrouping interface. This allows you to iterate through all items as IGrouping is derived from IEnumerable<> and has an extra field named Key:

public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
{
    TKey Key { get; }
}

Briefly said, a GroupBy method call returns a list of lists. An outer list corresponds to 'buckets' as you mentioned in your question. Then each 'bucket' contains items corresponding to that 'bucket'. To be specific to your example, the value of groupByResult is depicted in this screenshot. As we can see there, your initial collection was grouped into three different buckets that have 3, 2 and 3 items, respectively.

As for accessing items in these groups, you can use simple LINQ:

List<Item> firstBucketItems = groupByResult.First(i => i.Key == 1).ToList();
List<Item> secondBucketItems = groupByResult.First(i => i.Key == 2).ToList();
List<Item> thirdBucketItems = groupByResult.First(i => i.Key == 3).ToList();

First bucket items

Or you can just iterate through all items:

foreach (var itemGroup in groupByResult)
{
    int groupKey = itemGroup.Key;

    foreach (Item item in itemGroup)
    {
        // Do whatever...
    }
}
like image 35
anar khalilov Avatar answered Nov 09 '22 22:11

anar khalilov