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

时间:2016-09-19 22:27:11

标签: c# linq sorting equality

我正在使用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,.....),他们就会获得相同的位置。例如:
吉姆哈里斯,580,25,32,..... 汤姆詹森,560,20,30,..... 3.让·约翰逊,523,23,26,..... 3. Roy Beeman,523,23,26,.....
5. Doug Wilson,520,23,26,.....

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

一般而言,具有以下类别:

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

您如何识别相同的记录?处理完记录后,它应具有以下位置/顺序:
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

根据国际规则,参与者将按第一个字段中的值(第一个)进行排序。如果存在平局,则将比较第二列,因此我们对“秒”进行排序。如果仍然存在平局,则将比较第三列,因此我们对“第三”进行排序。如果仍有任何关系,运动员必须具有相同的排名,并且必须使用运动员的姓氏以拉丁字母顺序列出。

如果加工后的位置是1,2,3,3,3,6,7或1,2,3,3,3,4,5并不重要。 主要问题是在比较所有字段后知道是否有任何关系,以及涉及哪些记录。

我希望LINQ中有一些功能允许您在所有比较字段相等的情况下收到通知。

3 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:-1)

尝试这样的事情:(为了简单起见,我使用的是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列表已正确排序。

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

终于你可以遍历字典,每个项目的“位置/顺序”都是它的值,你可以将它添加到一个数组,或者你想要的任何数据类型。 (除了词典,因为键必须是唯一的

答案 2 :(得分:-1)

尝试这样做:

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;

我得到以下结果:

result

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