【问题标题】:C# GroupBy and Average a nested DictionaryC# GroupBy 和平均嵌套字典
【发布时间】:2020-08-20 05:23:45
【问题描述】:

我有以下数据。

new Dictionary<DateTime, Dictionary<string, int>>() 
{
    { 
        new DateTime(2020,05,05,10,10,10) , 
        new Dictionary<string, int>() 
        {
            { "Value1", 2 },
            { "Value2", 4 },
            { "ValueXY", 6 },
        }
    },
    { 
        new DateTime(2020,05,05,10,10,12) , 
        new Dictionary<string, int>() 
        {
            { "Value1", 4 },
            { "Value2", 6 },
            { "ValueABC", 12 }
        }
    }
};

我想用 LINQ 做的是按 DateTime 分组,没有秒数,每分钟有一个包。添加单个键的平均值。

所以通过上面的例子,我会得到这个

new Dictionary<DateTime, Dictionary<string, int>>()
{
    { 
        new DateTime(2020,05,05,10,10,0) , 
        new Dictionary<string, int>()
        {
            { "Value1", 3 },
            { "Value2", 5 },
            { "ValueABC", 12 },
            { "ValueXY", 6 }
        }
    }
};

我能够进行分组,但我没有得到平均部分的工作。

categorie.Value.GroupBy(row => row.Key.AddSeconds(-row.Key.Second));

任何想法如何做到这一点?

【问题讨论】:

    标签: c# linq dictionary nested


    【解决方案1】:

    您已经完成了分组,但您缺少的是对子字典中的值进行更复杂的组合和平均。您可以通过以下方式做到这一点。

    var x =new Dictionary<DateTime, Dictionary<string, int>>() 
    {
        { 
            new DateTime(2020,05,05,10,10,10) , 
            new Dictionary<string, int>() 
            {
                { "Value1", 2 },
                { "Value2", 4 },
                { "ValueXY", 6 },
            }
        },
        { 
            new DateTime(2020,05,05,10,10,12) , 
            new Dictionary<string, int>() 
            {
                { "Value1", 4 },
                { "Value2", 6 },
                { "ValueABC", 12 }
            }
        }
    };
    
    var res = x.GroupBy(kvp => 
            kvp.Key.AddSeconds(-kvp.Key.Second).AddMilliseconds(-kvp.Key.Millisecond))
        .ToDictionary(
            grp => grp.Key,
            grp => grp.SelectMany(kvp => kvp.Value)
                .GroupBy(kvp => kvp.Key)
                .ToDictionary(
                    grpInner => grpInner.Key, 
                    grpInner => grpInner.Average(kvp => kvp.Value)));
    

    这将进行您的分组,然后基于 DateTime 创建一个字典,无需几秒钟,然后它将展平内部字典中的所有键值对,然后对这些键值进行分组并生成平均值。

    编辑:正如 Vadim Martynov 所说,您还应该注意截断毫秒。

    【讨论】:

    • @PavelAnikhouski 是的,当我复制添加毫秒截断的更改时,我丢失了它。谢谢。
    • 感谢您的帮助,我仍然需要完全理解它,但我想我已经看到我在哪里使用了错误的路径:-)
    【解决方案2】:

    您的代码中有 3 个问题。

    1. AddSeconds 不适用于 DateTime 包含毫秒,因此您可以使用 more generic methods for round
    2. 您只是按DateTime 值而不是按内部字典键进行分组。
    3. 2 个整数值的平均值不是整数(例如,23 的平均值是 2.5)所以如果您确定结果是整数或使用 @改为 987654327@。

    这是您问题的完整代码:

    DateTime RoundDown(DateTime dt, TimeSpan d)
    {
        var delta = dt.Ticks % d.Ticks;
        return new DateTime(dt.Ticks - delta, dt.Kind);
    }
    
    Dictionary<DateTime, Dictionary<string, double>> result = categorie
        .GroupBy(kvp => RoundDown(kvp.Key, new TimeSpan(0, 0, 1, 0)), kvp => kvp.Value) // group by DateTime
        .ToDictionary( // retrieve result dictionary
            grouping => grouping.Key, // use DateTime from grouping as the key
            grouping => grouping.SelectMany(g => g) // flatten inner dictionaries to the IEnumerable<KeyValuePair<string, int>>
                .GroupBy(g => g.Key, g => g.Value) // group by string keys
                .ToDictionary(g => g.Key, g => g.Average())); // convert to the inner result dictionary with avg values
    

    【讨论】:

    • 他们也可以kvp.Key.AddSeconds(-kvp.Key.Second).AddMilliseconds(-kvp.Key.Millisecond)
    • @juharr 简短的回答是肯定的,稍微长一点 - 不会,因为实际上您需要同步到滴答声。但这取决于可以使用 new DateTime 手动创建的输入数据,并且值中不能有毫秒。我不知道所以我提出了最通用的解决方案。
    • 是的,任何超过毫秒的精度都需要您的方法。
    【解决方案3】:

    还可以使用Enumerable.ToLookup 对日期时间键的Date 部分进行分组,然后使用DateTime.AddHours 添加小时,使用DateTime.AddMinutes 添加分钟:

    var result = categorie
        .ToLookup(
            kvp => kvp.Key.Date
                .AddHours(kvp.Key.Hour)
                .AddMinutes(kvp.Key.Minute),
            kvp => kvp.Value)
        .ToDictionary(
            group => group.Key,
            group => group
                .SelectMany(group => group)
                .ToLookup(
                    kvp => kvp.Key, 
                    kvp => kvp.Value)
                .ToDictionary(
                    group => group.Key, 
                    group => group.Average()));
    

    首先使用Enumerable.ToDictionary将组转换为第一个外部Dictionary&lt;DateTime...&gt;,使用Enumerable.SelectMany展平内部值,使用ToLookup创建键和值的查找,然后创建内部Dictionary&lt;string, int&gt;并获取Enumerable.Average 的平均值。最终结果将是Dictionary&lt;DateTime, Dictionary&lt;string, double&gt;&gt;

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 2023-03-15
      • 2021-12-24
      • 1970-01-01
      • 1970-01-01
      • 2016-06-17
      • 2018-10-30
      • 2013-03-08
      • 1970-01-01
      相关资源
      最近更新 更多