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;
}
}
}
我正在使用 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;
}
}
}