LINQ 排序。如何确定多个字段的相等性

LINQ sorting. How to determine equality for multiple fields

我正在使用 LINQ 对多个字段进行排序。但是我如何确定列表中的两个或多个条目是否相等?即所有字段都是相等的,排序顺序不会因为相等而改变。

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>();
}

内容来自保存竞赛结果的数据库,并根据竞赛规则进行排序。排序是为了决定参赛者的排名。

根据规则,如果一个或多个参赛者在比赛中获得相同的值(p.totsum、p.totOmskyting、.....),他们将获得相同的位置。例如:
1. 吉姆·哈里斯,580 岁,25 岁​​,32 岁,.....
2. 汤姆·詹森,560 岁,20 岁,30 岁,.....
3. 让·约翰逊,523 岁,23 岁,26 岁,.....
3. 罗伊·比曼,523 岁,23 岁,26 岁,.....
5. 道格·威尔逊,520 岁,23 岁,26 岁,.....

我排序后遇到的问题是,我不确定如何识别此示例中位置 3 中的两个人。

通俗地说,具有以下 class:

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

它将使用

排序
.OrderByDescending(p => p.first)
.ThenByDescending(p => p.second)
.ThenByDescending(p => p.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

如何识别相等的记录?处理记录后,它应该有以下 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

根据国际规则,参赛者将按第一个字段(first)的值排序。如果有平局,将比较第二列,所以我们按 "second" 排序。如果仍然平局,将比较第三列,因此我们按 "third" 排序。如果仍然存在任何平局,则运动员的排名必须相同,并且必须使用运动员的姓氏按拉丁字母顺序列出。

如果处理后的位置是1,2,3,3,3,6,7还是1,2,3,3,3,4,5不重要。 主要问题是了解在比较所有字段后是否还有任何联系,以及涉及哪些记录。

我希望 LINQ 中有一些功能可以让您在所有比较字段都相等时得到通知。

尝试这样的事情:(为了简单起见,我正在使用您的 MigrObject 示例)

Dictionary<MigrObject, int> orderedResult = new Dictionary<MigrObject, int>(); //reverse dictionary where int is the 'order' you want
foreach (var item in resultat) // assuming resultat is a List of MigrObject
{
    int order = 1;
    if(!orderedResult.Any(o => o.first == item.first && o.second == item.second && o.third == item.third)) // I don't use ContainsKey(item) because it may not work for custom defined classes as key
    {
        orderedResult.Add(item, order);
        count++;
    }
    else
        orderedResult.Add(item, order);
}

它可能看起来很复杂,但这里是解释:

-字典是颠倒的,因为同一个键我们可以有多个值,但反过来不行

-从 1 开始下单,因为没有第 0 位(在您的示例中)

- 检查字典是否包含每个项目的记录:如果没有,将其添加并增加计数 1,如果是,则不增加计数(因此给出 2 个重复项相同的顺序)

-假设 resultat 列表排序正确。

-当你比较的字段很多时,这个方法可能会变得复杂,但是如果你有更好的比较方法,你可以修改它。

最后 你可以遍历字典,每个项目的 "positions/order" 是它的值,你可以将它添加到一个数组,或者你想要的任何数据类型。 (字典除外,因为键必须是唯一的

尝试这样做:

var scores = new [] { 580, 560, 523, 523, 523, 520, 518, 518, 430, };

var results =
    scores
        .OrderByDescending(x => x) // just in case scores not in order
        .GroupBy(x => x)
        .Aggregate(
            new { Rank = 1, Results = new List<Tuple<int, int>>() },
            (a, gx) =>
            {
                a.Results.AddRange(gx.Select(x => Tuple.Create(a.Rank, x)));
                return new { Rank = a.Rank + gx.Count(), Results = a.Results };
            })
        .Results;

我得到以下结果:

您需要调整代码以适应您的输入。

请试试这个。 我已经测试过并且有效。

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;
            }
        }
    }