[英]linq groupby and max count
我有一个 class 之类的
public class Test
{
public string name;
public int status;
}
示例数据
new Test("Name1", 1);
new Test("Name2", 2);
new Test("Name3", 3);
new Test("Name4", 1);
new Test("Name5", 2);
new Test("Name6", 2);
new Test("Name7", 3);
我正在寻找一些 linq 来返回值 2 - 这是出现次数最多的状态。
目前我有以下不正确的。
var status = listTest.GroupBy(x => x.status).Select(x => x.OrderByDescending(t => t.status).First()).FirstOrDefault().status;
但希望有更清洁的东西?
我想这就是你想要的
您需要自己对组进行排序,而不是对每个组中的内容进行排序。
var status = listTest
.GroupBy(x => x.Status)
.OrderByDescending(g => g.Count())
.FirstOrDefault()?.Key;
您可以在降序排序后分组并选择顶部
var value = list.GroupBy(q => q.status)
.OrderByDescending(gp => gp.Count())
.First().Key;
要求:给定 class
Test
的对象序列,其中每个 Test 都有一个 int 属性Status
,给我 Status 出现次数最多的值。
为此,创建具有相同属性 Status 值的 Groups Test 对象。 计算每组中元素的数量。 对结果进行排序,使数字最大的组排在第一位,并取第一个元素。
IEnumerable<Test> testSequence = ...
var statusThatOccursMost = testSequence
// make Groups of Tests that have the same value for Status:
.GroupBy(test => test.Status,
// parameter resultSelector: for every occurring Status value and all
// Tests with this common status value, make one new object,
// containing the common Status value and the number of Tests that have
// this common Status value
(commonStatusValue, testsThatHaveThisCommonStatusValue) => new
{
Status = commonStatusValue,
Count = testsThatHaveThisCommonStatusValue.Count(),
})
结果:一系列 [Status, Count] 组合。 Status 在 testSequence 中至少出现一次。 Count 是 Status 出现的次数。 所以我们知道,Count >= 1。
按 Count 值降序排列此 [Status, Count] 组合序列,因此第一个元素是 Count 值最大的元素:
.OrderByDescenting(statusCountCombination => statusCountCombination.Count)
结果:一系列 [Status, Count] 组合,其中 Count 值最大的组合排在第一位。
从组合中提取 Status 的值,并取第一个:
.Select(statusCountCombination => statusCountCombination.Status)
.FirstOrDefault();
尽管这个 LINQ 相当简单,但如果您只想要 Count 值最大的那个,那么计算所有 Status 值并对所有 StatusCount 组合排序并不是很有效。
考虑创建一个扩展方法。 如果您不熟悉扩展方法,请阅读扩展方法揭秘
制作字典:关键是状态。 值是此状态发生的次数。 然后取Count最大的Status
public static int ToMostOccuringStatusValueOrDefault(
this IEnumerable<Test> testSequence)
{
// return default if testSequence is empty
if (!testSequence.Any()) return 0;
Dictionary<int, int> statusCountCombinations = new Dictionary<int, int>();
foreach (Test test in testSequence)
{
if (statusCountCombinations.TryGetValue(test.Status, out int count)
{
// Status value already in dictionary: increase count:
statusCountCombinations[test.Status] = count + 1;
}
else
{
// Status value not in dictionary yet. Add with count 1
statusCountCombinations.Add(test.Status, 1);
}
}
GroupBy 的工作方式与上面类似,除了它会首先创建一个字典,其中每个值都是一个测试列表。 然后如果计算测试的数量,并丢弃列表。 在扩展方法中,我们不必创建列表。
继续扩展方法:找到Value最大的KeyValuePair。 我们可以使用 Enumerable.Aggregate,或者枚举:
using (var enumerator = statusCountCombinations.GetEnumerator())
{
// we know there is at least one element
enumerator.MoveNext();
// the first element is the largest until now:
KeyValuePair<int, int> largest = enumerator.Current;
// enumerate the rest:
while (enumerator.MoveNext)
{
if (enumerator.Current.Value > largest.Value)
{
// found a new largest one
largest = enumerator.Current;
}
}
return largest.Key;
}
}
在这种方法中,我们只需枚举一次 testSequence 和一次 Dictionary。 如果您使用 Linq GroupBy / OrderByDescending,GroupBy 的结果将被枚举多次
用法:
IEnumerable<Test> testSequence = ...
var mostCommonStatus = testSequence.ToMostOccurringStatusValueOrDefault();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.