Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying LINQ to Objects Group By and Sort By to generic List<T> (C#)

I am having a hard time creating working Group By and Sort By clauses against a generic List myList. myList has a list of property 'Settings' which itself contains a list of 'child' properties for each business.

I want to group by a Industry and within each Industry, sort by business name. My intent would be this:

string groupSetting = "Industry";
sortSetting = "BusinessName";
myList.GroupBy(p => p.Settings.Find(s => s.Name == groupSetting)).OrderBy(p => p.Settings.Find(t => t.Name == sortSetting));

However I am getting the error : 'System.Linq.IGrouping does not contain a definition for Settings and no extension method Settings accepting a first argument of type System.Linq.Igrouping could be found....' indicating I cannot invoke the order by clause without some transformation or additional treatment.

I have tried all sorts of things to split this out and get it working, but I am missing something. Any help appreciated

like image 557
Ash Machine Avatar asked May 19 '09 04:05

Ash Machine


1 Answers

Your problem is that GroupBy doesn't return a single list of settings, it returns a "list-of-lists". This is the IGrouping which you are seeing.

You need to iterate over each group in the IGrouping, sort that group, and then iterate over each item within the group. Observe:

public static void Main( string[] args )
{
    var groupSetting = "Industry";

    // step 1: group the data. Note this doesn't actually create copies of the data as
    // it's all lazy loaded
    // BEWARE. If a company doesn't have the "Industry" setting, this will throw an exception
    var grouped = companies.GroupBy(c => c.Settings.First(s => s.Name == groupSetting).Value);
    foreach( var group in grouped )
    {
        Console.WriteLine(group.Key);// this is the Value that came out of the GroupBy

        // Note how we have to do the ordering within each individual group. 
        // It doesn't make sense to try group them in one hit like in your question
        foreach( var item in group.OrderBy(bus => bus.Name) )
            Console.WriteLine(" - " + item.Name);
    }
}

Data structures provided for clarity:

struct Setting { public string Name; public string Value; }
struct Business { public string Name; public IEnumerable<Setting> Settings; }

static IEnumerable<Business> companies = new[]{
    new Business{ Name = "XYZ Inc.", Settings = new[]{ 
        new Setting{ Name="Age", Value="27"},
        new Setting{ Name="Industry", Value="IT"}
    }},
    new Business{ Name = "Programmers++", Settings = new[]{ 
        new Setting{ Name="Age", Value="27"},
        new Setting{ Name="Industry", Value="IT"}
    }},
    new Business{ Name = "Jeff's Luxury Salmon", Settings = new[]{ 
        new Setting{ Name="Age", Value="63"},
        new Setting{ Name="Industry", Value="Sports"}
    }},
    new Business{ Name = "Bank of Khazakstan", Settings = new[]{ 
        new Setting{ Name="Age", Value="30"},
        new Setting{ Name="Industry", Value="Finance"}
    }},
};

This produces the following output: Copy/paste the code and run it and play with it

IT
 - Programmers++
 - XYZ Inc.
Sports
 - Jeff's Luxury Salmon
Finance
 - Bank of Khazakstan
like image 56
Orion Edwards Avatar answered Oct 31 '22 07:10

Orion Edwards