1

I have a list of instances of a class like this.

public class Customer
{
  public Guid Id { get; set; }
  public Guid StoreId { get; set; }
  public string Name { get; set; }
}

I need to break it up by the stores so that I have a set of lists, where each list contains all the customers belonging to the same store. I've tried grouping but that produces just a list of lists. I've tried dictionaries but that gave me Dictionary<Guid, Customer>, when I need something like Dictionary<Guid, List<Customer>>. I tried looking at ILookup and even something called SortedDictionary.

In the end I got confused and wimzy.

I'd like to be able to perform an iteration like this.

foreach(KeyValuePair storeWithCustomers in brokenUpList)
{
  DoSomething(storeWithCustomers.Key);
  foreach(Customer customer in storeWithCustomers)
    DoSomething(customer.Name);
}

It doesn't have to be KeyValuePair, of course, and the breaking up of the list doesn't have to be ToDictionary (although it may, if it's a good solution).

Community
  • 1
  • 1

2 Answers2

2

Simple, you just need to .GroupBy like this:

foreach(var group in customers.GroupBy(x=>x.StoreId))
{
  DoSomething(group.Key);
  foreach(var customer in group)
    DoSomething(customer.Name);
}
Robert McKee
  • 21,305
  • 1
  • 43
  • 57
  • @Got it! Really well put! I've taken the liberty to paste that in your answer, because I'd definitely find that helpful if I was researching and hit this question. Feel free to flag this comment as obso. –  Jan 23 '16 at 16:25
1

Use the ToDictionary<TSource, TKey> method after you call GroupBy to select the grouped results into the Dictionary of the desired type.

Note that the method takes two (lambda) parameters (well, the particular overload used here, at least). The first is a selector that defines the keys (and implicitly, their type), and the second defines the values (implicitly defining their type as well).

As shown in the other answer, the GroupBy method will return IEnumerable<IGrouping<Guid,Customer>>, which you can iterate over directly, like so:

foreach (IGrouping<Guid, Customer> grp in customers.GroupBy(obj => obj.StoreId))
{
    Guid storeID = grp.Key;
    IEnumerable<Customer> customerCollection = grp;
    foreach (Customer customer in customerCollection)
    {
        // Insert code here.
    }
}

The above is likely the better approach (e.g. if your collection is lazily evaluated, it will probably retain that), but if that isn't an issue for you, the solution below will give you exactly what you asked for.

In the example below, the only line you're probably interested in is the last one. The other code is merely to facilitate running it quickly in something like LinqPad to preview the results.

// Just populating some sample data.
List<Guid> storeIDs = new List<Guid>
{
    Guid.NewGuid(),
    Guid.NewGuid(),
    Guid.NewGuid(),
};
List<Customer> customers = new List<Customer>();

customers.Add(new Customer { Id = Guid.NewGuid(), StoreId = storeIDs[0], Name = "John" });
customers.Add(new Customer { Id = Guid.NewGuid(), StoreId = storeIDs[1], Name = "Jacob" });
customers.Add(new Customer { Id = Guid.NewGuid(), StoreId = storeIDs[2], Name = "Schmidt" });
customers.Add(new Customer { Id = Guid.NewGuid(), StoreId = storeIDs[0], Name = "May" });
customers.Add(new Customer { Id = Guid.NewGuid(), StoreId = storeIDs[1], Name = "Naomi" });
customers.Add(new Customer { Id = Guid.NewGuid(), StoreId = storeIDs[2], Name = "Tou" });

// Use the GroupBy method to group the customers by the StoreID
// Then, select the grouped data into a Dictionary.
// Use the StoreID as the key
// To make TValue a List<Customer> call ToList on the IGrouping.
Dictionary<Guid, List<Customer>> result = customers.GroupBy(obj => obj.StoreId).ToDictionary(k => k.Key, v => v.ToList());
Joshua Shearer
  • 1,120
  • 10
  • 23