繁体   English   中英

linq 分组和最大计数

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM