要求:给定一个Test类的对象序列,其中每个Test都有一个int属性Status,给我出现最多的Status值。
为此,使 Groups Test 对象具有相同的属性 Status 值。计算每组中的元素数。对结果进行排序,使数字最大的组在前,并取第一个元素。
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] 组合。状态在 testSequence 中至少出现一次。 Count 是 Status 出现的次数。所以我们知道,Count >= 1。
这个[Status, Count]组合序列按Count的降序排列,所以第一个元素是Count的最大值:
.OrderByDescenting(statusCountCombination => statusCountCombination.Count)
结果:[Status, Count] 组合的序列,其中 Count 值最大的组合在前。
从组合中提取Status的值,取第一个:
.Select(statusCountCombination => statusCountCombination.Status)
.FirstOrDefault();
优化
虽然这个 LINQ 相当简单,但如果您只想要 Count 值最大的一个,那么对所有 Status 值进行计数并对所有 StatusCount 组合进行排序并不是很有效。
考虑创建一个扩展方法。如果你不熟悉扩展方法,请阅读Extension Methods Demystified
制作字典:关键是状态。值是此状态发生的次数。然后取 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 的工作方式与上述类似,不同之处在于它将首先创建一个字典,其中每个值都是一个测试列表。然后 if 计算测试的数量,并丢弃列表。在扩展方法中,我们不必制作 List。
继续扩展方法:找到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();