E.Skaar E.Skaar - 2 months ago 12
C# Question

LINQ sorting. How to determine equality for multiple fields

I'm sorting multiple fields with LINQ. But how can I determine if two or more entries in the List are equal? That is, all fields are equal and sorting order will not change because of equality.

public List<SfpDb.ResultatViewRang> sortResMest(List<SfpDb.ResultatViewRang> resultat)
{
return resultat
.OrderBy(p => p.statdsq)
.ThenBy(p => p.statdns)
.ThenBy(p => p.statdnf)
.ThenByDescending(p => p.totsum)
.ThenByDescending(p => p.totOmskyting)
.ThenByDescending(p => p.totinnertreff)
.ThenByDescending(p => p.sumr10)
.ThenByDescending(p => p.sumr9)
.ThenByDescending(p => p.sumr8)
.ThenByDescending(p => p.sumr7)
.ThenByDescending(p => p.sumr6)
.ThenByDescending(p => p.sumr5)
.ThenByDescending(p => p.sumr4)
.ThenByDescending(p => p.sumr3)
.ThenByDescending(p => p.sumr2)
.ThenByDescending(p => p.sumr1)
.ThenBy(p => p.perTreffRangStr)
.ThenBy(p => p.enavn, strCompareNo)
.ThenBy(p => p.fnavn, strCompareNo)
.ToList<SfpDb.ResultatViewRang>();
}


The content is from a database which holds results from a competition, and is sorted according to the competition rules. The sorting is used to determine which position the competitors get.

According to the rules, if one ore more competitors have the same values from the competition (p.totsum, p.totOmskyting, .....), they get the same position. Eg:

1. Jim Harris, 580, 25, 32, .....

2. Tom Jensen, 560, 20, 30, .....

3. Jean Johnson, 523, 23, 26, .....

3. Roy Beeman, 523, 23, 26, .....

5. Doug Wilson, 520, 23, 26, .....


The problem I have after sorting, is that I'm not sure how to identify the two persons in position 3 in this example.

In generic terms, having the following class:

public class MigrObject
{
public int first { get; set; }
public int second { get; set; }
public int third { get; set; }
}


It would be sorted using

.OrderByDescending(p => p.first)
.ThenByDescending(p => p.second)
.ThenByDescending(p => p.third)


Having the following records: (first, second, third)

580, 25, 32

560, 30, 30

523, 23, 26

523, 23, 26

523, 23, 26

520, 23, 26

518, 40, 30

518, 40, 30

430, 14, 16


How do you identify the records that are equal? After processing the records, it should have the following positions/order:

1: 580, 25, 32

2: 560, 30, 30

3: 523, 23, 26

3: 523, 23, 26

3: 523, 23, 26

6: 520, 23, 26

7: 518, 40, 30

7: 518, 40, 30

9: 430, 14, 16


According to the international rules, the participants will be sorted by the value in the first field(first). If there is a tie, the second column will be compared, so we sort on "second". If there is still a tie, the third column will be compared, so we sort on "third". If any ties remain, the athletes must have the same ranking and must be listed in Latin alphabetical order using the athlete’s family name.

If the positions after the processing is 1,2,3,3,3,6,7 or 1,2,3,3,3,4,5 is not important. The main problem is knowing if there is any ties left after comparing all the fields, and which records are involved.

I was hoping that there were some features within LINQ which allowed you to be notified if all compared fields are equal.

Answer

Please try this. I've tested and it works.

public class Program
    {
        public class TestData
        {
            public int First { get; set; }
            public int Second { get; set; }
            public int Third { get; set; }
            public int Place { get; set; }
        }

        public static void Main(string[] args)
        {
            var data = new List<TestData>
            {
                new TestData {First = 580, Second = 25, Third = 32},
                new TestData {First = 560, Second = 30, Third = 30},
                new TestData {First = 523, Second = 23, Third = 26},
                new TestData {First = 523, Second = 23, Third = 26},
                new TestData {First = 523, Second = 23, Third = 26},
                new TestData {First = 520, Second = 23, Third = 26},
                new TestData {First = 518, Second = 40, Third = 30},
                new TestData {First = 518, Second = 40, Third = 30},
                new TestData {First = 430, Second = 14, Third = 16}
            };

            var sorted =
                (from d in data
                    orderby d.First descending, d.Second descending, d.Third descending 
                    group d by d.First.ToString() + d.Second + d.Third into gd
                    select gd.ToList())
                    .ToList();

            var result = new List<TestData>();
            var place = 1;
            foreach (var sd in sorted)
            {
                sd.ForEach(p => p.Place = place);
                result.AddRange(sd);
                place += sd.Count;
            }
        }
    }