7

I have an array of MyClass, which can be simplified as follow:

public class MyClass {
    public int Id;
    public string Origin;
    public int Points;
    public DateTime RequestTime;
    public MyClass(int id, string origin, int points, DateTime requestTime) {
        Id = id;
        Origin = origin;
        Points = points;
        RequestTime = requestTime;
    }
}

Now, in the Array, without any errors from the user side or throughout the input process, there cannot be MyClass instance with identical Id and Origin.

However, if there be any, I should resolve it. And here are the resolving rules:

  1. Firstly by Points - that is, to take one among the duplicates which has the highest Points

  2. But if the Points are the same, I have to further resolve it by using RequestTime - the latest will be taken.

  3. And if, there is no difference in RequestTime, then I can take one of the duplicates arbitrarily.

Here is the sample data input I have:

MyClass[] myarr = new MyClass[] {
    new MyClass(1, "Ware House 1", 5, new DateTime(2016, 1, 26, 14, 0, 0)), //[0]
    new MyClass(1, "Ware House 1", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[1] //higher points
    new MyClass(1, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[2]
    new MyClass(1, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 1, 0)), //[3] //later time
    new MyClass(1, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[4]
    new MyClass(2, "Ware House 2", 7, new DateTime(2016, 1, 26, 14, 0, 0)), //[5] //higher points
    new MyClass(2, "Ware House 2", 5, new DateTime(2016, 1, 26, 14, 1, 0)), //[6] //later time but less points
    new MyClass(3, "Ware House 1", 6, new DateTime(2016, 1, 26, 14, 0, 0)), //[7] //triplet, pick any
    new MyClass(3, "Ware House 1", 6, new DateTime(2016, 1, 26, 14, 0, 0)), //[8] //triplet, pick any
    new MyClass(3, "Ware House 1", 6, new DateTime(2016, 1, 26, 14, 0, 0)) //[9] //triplet, pick any
};

The final result should be [1], [3], [5], + any of [7]/[8]/[9]

I want to implement LINQ solution for it, but stuck. I do not know how make query it at once.

Any idea?

Ian
  • 30,182
  • 19
  • 69
  • 107

2 Answers2

8

Group by {Id, Origin} and take the first one from each group when ordered by Points and RequestTime:

var query = from x in myarr
    group x by new {x.Id, x.Origin}
    into g
    select (
        from z in g
        orderby z.Points descending, z.RequestTime descending
        select z).First();

In method syntax, this is:

var query =
    myarr.GroupBy(x => new {x.Id, x.Origin})
         .Select(g => g.OrderByDescending(z => z.Points)
                       .ThenByDescending(z => z.RequestTime)
                       .First());
lc.
  • 113,939
  • 20
  • 158
  • 187
  • ah, I can see that the trick is to `group by` both the Id and Origin before decendingly ordering it twice. I did not know before that we could group by two items. I was thinking of doing double `GroupBy` which did not seem to be good idea because of the nature of the "composite key". Thanks a lot again! :) – Ian Jan 26 '16 at 07:24
  • 1
    @Ian Yep, anonymous types are your friend in grouping and joining. – lc. Jan 26 '16 at 07:30
0

Try following:

myarr.OrderBy(m=> m.Points).ToList();

or

myarr.OrderBy(m=> m.Points).Orderby(m=> m.RequestTime);
Carsten Cors
  • 2,505
  • 2
  • 14
  • 21
  • Thanks for the prompt answer... To order it alone is not difficult, but where I was stuck was when I tried to remove duplicates based on the resolving rules. Any idea? – Ian Jan 26 '16 at 07:14
  • dit you see following post: http://stackoverflow.com/questions/1606679/remove-duplicates-in-the-list-using-linq – Carsten Cors Mar 17 '16 at 10:38