Search code examples
linqsortinglinq-group

Sorting Groups to display them by date of containing element (LINQ)


I'm not sure if this is possible. My class I have a list of looks like this:

class Person
{
 string Firstname
 string Lastname
 DateTime Timestamp
}

Now I would like to create groups by Firstname and Lastname.

  • John Deer, 3:12
  • John Deer, 6:34
  • John Deer, 11:12
  • Tom Kin, 1:12
  • Tom Kin, 3:49
  • Tom Kin, 4:22
  • Markus Fert, 11:23

Further more I would like to sort this groups by their Timestamp, the last should be first while the groups should stay to display them in a listView.

  • Markus Fert (Group Header)
    11:23 (Content Element)

  • John Deer
    11:12
    6:34

  • Tom Kin
    4:22
    3:49

  • John Deer
    3:12

  • Tom Kin
    1:22

Hope any Linq genius can help me solving the problem :) Thanks!!


Much Thanks to Sergey, worked like a charm!

Further I would like to create a custom Class for my group Key to display different additional things in my ListView headers. (not only a spliced together string)

I would like to assign my query to an IEnumerable like this:

IEnumerable<IGrouping<Header, Person>> PersonGroups 

Where the header contains some other properties contained in each Person (e.g. there is also a Country, Age,... for each Person). Maybe you can help me there too?


Thanks again Sergey. Solved my problem by implementing an Header class which implements the ICompareable interface.

IEnumerable<IGrouping<Header, Person>> PersonGroups

 public class Header: IComparable<Header>
 {
     public Header(string firstname, string lastname)
     {
        Firstname= firstname;
        Lastname = lastname;
     }

     public string Firstname{ get; set; }
     public string Lastname{ get; set; }

     public int CompareTo(Header that)
     {
       if (this.Firstname == that.Firstname&& this.Lastname == that.Lastname)
          return 0;
       else
           return -1;
     }
  }

My query now looks like this:

PersonGroups= persons.OrderByDescending(p => p.Timestamp)
                .GroupConsecutive(p => new Header(p.Firstname, p.Lastname));

Solution

  • Actually you need to order results by timestamp first. And only then group this ordered sequence by consecutive people:

    var query = 
      people.OrderByDescending(p => p.Timestamp.TimeOfDay)
            .GroupConsecutive(p => String.Format("{0} {1}", p.Firstname, p.Lastname))
            .Select(g => new {
                 Header = g.Key,
                 Content = String.Join("\n", g.Select(p => p.Timestamp.TimeOfDay))
            });
    

    You will need GroupConsecutive implementation, which creates groups of consecutive items based on same value of provided selector (full name in your case).

    For your sample input result is:

    [
      {
        "Header": "Markus Fert",
        "Content": "11:23:00"
      },
      {
        "Header": "John Deer",
        "Content": "11:12:00\n06:34:00"
      },
      {
        "Header": "Tom Kin",
        "Content": "04:22:00\n03:49:00"
      },
      {
        "Header": "John Deer",
        "Content": "03:12:00"
      },
      {
        "Header": "Tom Kin",
        "Content": "01:12:00"
      }
    ]