List GroupBy?

List GroupBy?

Old forum URL: forums.lhotka.net/forums/t/3716.aspx


SonOfPirate posted on Monday, October 15, 2007

Has anyone implemented or given any thought to how you would implement GroupBy behavior in a business list?

We have a couple of use-cases where we are applying a multi-column sort to our list then checking for changes in the column values as we iterate through the list so that we can render the items in the desired groupings.

As an example, let's say we extended the sample ProjectTracker application to identify the Customer for each Project and we want to be able to view projects, grouped by customer and the start month.  So, our GroupBy phrase would be, "Customer, Month(StartDate)" - or something.  To do this, we have the following code in place now:

string customer = "";
int month = 0;


foreach (Project p in Projects)
{
    if (p.Customer != customer)
    {
        newCustomer = true;
        newMonth = true;
    }
    else if (p.StartDate.Month != month)
        newMonth = true;

    if (newCustomer)
    {
        // Render Customer group row
    }

    if (newMonth)
    {
        // Render Month group row
    }

    // Render (normal) Project row
}

Projects is a SortedList with "Customer, StartDate, Name" as the sort expression.

It would be great if we could something like:

foreach (X customerGroup in Projects.GroupBy("Customer"))
{
    // Render Customer group row
   
    foreach (X monthGroup in customerGroup.GroupBy("Month(StartDate)"))
    {
        // Render Month group row

        foreach (Project p in monthGroup)
            // Render (normal) Project row
    }
}

At a minimum, I'd be interested if anyone has even implemented just a single level GroupBy (e.g. by Customer).

Any thoughts, ideas or suggestions???

Thx.

 

ajj3085 replied on Monday, October 15, 2007

Hmm... sounds like something Linq would be handy for. 

SonOfPirate replied on Monday, October 15, 2007

Yea, I'd thought of that.  But (and I admit I am still learning Linq, so I may be off a bit), the "collection" created by Linq would be no different than what we already possess - a list of objects with properties that correspond to the groupings and sorted on those properties.  I don't believe Linq behaves any differently than if we used a GroupBy clause in SQL (not meant to be true SQL!):

SELECT * FROM Projects ORDER BY Customer, StartDate, Name GROUP BY Customer, StartDate

would result in:

Customer StartDate Name etc.
Acme Acres 1/1/07 Project1 ...
Acme Acres 6/1/07 Project2 ...
Acme Industries 3/1/07 Project3 ...
Acme Industries 3/15/07 Project4

This is the same as if we used a SortedList sorted on Customer, StartDate and Name.  The key is how to now access the data in a GroupBy way - almost heirarchically - with the underlying data that our business list contains unchanged.

Thx.

 

ajj3085 replied on Monday, October 15, 2007

From what I've read, you could create a list of Customers, which contains a list of Projects, etc.  But I haven't done much with it.

If you don't want to do Linq, you could make a view object (in your UI layer) that could be created by passing it the list you do have.. and it could create the object hierarchy that you want.

JoeFallon1 replied on Monday, October 15, 2007

Not sure if I missed the point here entirely but...

On my web screens I sometimes use nested repeaters to give the effect of grouping.

This uses 2 collections. The first collection provides the Group ID and the 2nd is filtered by that ID to return only the rows for that group. This is repeated until the first collection is exhausted.

Joe

 

Curelom replied on Monday, October 15, 2007

In WPF, you could use a CollectionView/CollectionViewSource.

SonOfPirate replied on Monday, October 15, 2007

Andy - Yea, we could build the heirarchy that way but that still doesn't help us group by date, for instance.  And, in this case (although I could change it), the Customer field is strictly a text-field - so perhaps Customer was a bad example.

Joe - Who's doing the filtering? And where does the GroupID come from?  That's getting to where I am. 

If I can make a quick assumption from what you are describing, it sounds like you have a notion of groups coded or defined in your app.  For our needs, the notion of a group is completely dynamic and user-defined at run-time.  There is no "GroupID" per se.

Curelom - I'm not proficient in WPF yet, so I'll have to take a closer look at those two classes and how they work.  While I do not believe that they will do the trick because they are part of the UI framework and we need this behavior in our BO libraries to share across multiple platforms, presentation layers, etc, it does appear that I might be able to get some insight in how MS has structured this behavior by looking at these classes.  To early to tell.

Thx.

 

 

Curelom replied on Monday, October 15, 2007

Perhaps with linq, you could use one linq query to gather the groups from the list, then use another linq query to grab all the items from each of the groups.  So if you had 3 groups, you would run 4 queries, one for the groups, then one for each group.  I don't know what performance would be like.

SonOfPirate replied on Monday, October 15, 2007

Perhaps someone can give me an example of the LINQ code used to accomplish this.  I still am not seeing how this saves me anything and that may be the key to my understanding.

Again, the goal is to provide a single, encapsulated business object/list that can be provided to another BO or UI developer to consume.  I don't want them to have to know or handle the logic described in my initial post - for a couple of reasons.  One, it slows development time.  And, two, there's a ton of duplication when this structure exists all over our applications.

 

Curelom replied on Monday, October 15, 2007

With groupby in linq, it actuall gets you an IGrouping IEnumerable that you can use in a foreach to grab each of the groups.

for example the following code groups by the length of the string

string[] names = { "Albert", "Burke", "Connor", "David",
                   "Everett", "Frank", "George", "Harris"};

// group by length
var groups = names.GroupBy(s => s.Length, s => s[0]);
foreach (IGrouping<int, char> group in groups) {
    Console.WriteLine("Strings of length {0}", group.Key);

    foreach (char value in group)
        Console.WriteLine("  {0}", value);

would produce the following output

Strings of length 6
  A
  C
  G
  H
Strings of length 5
  B
  D
  F
Strings of length 7
  E

More details at http://msdn2.microsoft.com/en-us/library/bb308959.aspx

 

SonOfPirate replied on Monday, October 15, 2007

EXCELLENT!

Now I see what you were getting at and that does address my lack of understanding.  I will delve into the article more...

Pardon my ignorance of LINQ (I'm reading as fast as I can!), but where does GroupBy come from and what type(s) does it apply to?  Can it be used on any enumerable?

 

So, does this mean the end of SortedList, FilteredList, etc???

 

ajj3085 replied on Tuesday, October 16, 2007

Its an extension method declared in one of the Linq assemblies. I BELIEVE it does in fact extend IEnumberable<T>, so anything that implements that should be good to go.

Copyright (c) Marimer LLC