Domanda

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));
È stato utile?

Soluzione

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"
  }
]

Altri suggerimenti

Here's an approach using the built-in link operator Aggregate.

First I need to order the list by descending timestamp and then I created a name formatter function.

var op = people
    .OrderByDescending(p => p.Timestamp)
    .ToArray();

Func<Person, string> toName = p =>
    String.Format("{0} {1}", p.Firstname, p.Lastname);

Now I can build the query:

var query =
    op
        .Skip(1)
        .Aggregate(new []
        {
            new
            {
                Name = toName(op.First()),
                Timestamps = new List<string>()
                {
                    op.First().Timestamp.ToShortTimeString(),
                },
            },
        }.ToList(), (a, p) =>
        {
            var name = toName(p);
            if (name == a.Last().Name)
            {
                a.Last().Timestamps.Add(p.Timestamp.ToShortTimeString());
            }
            else
            {
                a.Add(new
                {
                    Name = name,
                    Timestamps = new List<string>()
                    {
                        p.Timestamp.ToShortTimeString(),
                    },
                });
            }
            return a;
        });

I got this result:

result

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top