Nested Dictionary: group by date and then group by Enum value and summarize the count

时间:2017-12-18 06:10:50

标签: c# linq enums

My raw data looks like this:

IDictionary<DateTime, IDictionary<ReportType, int>> rawData

Example:

19/09/2017, [(ReportType.Open, 2)] 
25/09/2017, [(ReportType.Open, 15), (ReportType.Sent, 15)] 
25/10/2017, [(ReportType.Open, 27), (ReportType.Sent, 15)] 
29/10/2017, [(ReportType.Sent, 150)]

I'm trying to achieve The following result:

data for open:
september-2017: 17
october-2017: 27

data for sent:
september-2017: 15
october-2017: 165
-----------------------------

I've tried to

var openData = rawData
    .Select(g => new {
        dt = string.Format("{0}/{1}", g.Key.Month, g.Key.Year),
        g.Key.Year,
        g.Key.Month,
        action = g.Value.FirstOrDefault().Key,
        total = g.Value.Values.Sum()
    })
    .GroupBy(l => new {
        l.Month,
        l.Year,
        action = l.action
    })
    .Where(a => a.Key.action == ReportType.Open)
    .OrderByDescending(a => a.Key)
    .ToList();

What am i doing wrong?

The Print section (series1 is derived from Series class):

foreach (var item in openData)
{
      series1.Points.Add(new DataPoint()
      {
          YValues = new double[] { item.total },
          AxisLabel = item.dt.ToString(),
          ToolTip = item.total.ToString()
      });
}

4 个答案:

答案 0 :(得分:1)

You are filtering the Open reporttype wrong place;

            var openData = rawData
            .Select(g => new {
                dt = string.Format("{0}/{1}", g.Key.Month, g.Key.Year),
                g.Key.Year,
                g.Key.Month,
                actions = g.Value.Where(x => x.Key == ReportType.Open) //Filter it here
            })
            .GroupBy(l => new {
                l.Month,
                l.Year
            })
            .Select(s =>
            new
            {
                s.Key.Month,
                s.Key.Year,
                s.Sum(x => x.actions.Sum(sum => sum.Value))
            })
            .ToList();

答案 1 :(得分:1)

Sometimes a simple nested foreach can still be the best & clearest solution. It will, in most cases with large datasets, be quicker than LINQ solutions.

Dictionary<string, int> openCounts = new Dictionary<string, int>();
Dictionary<string, int> sentCounts = new Dictionary<string, int>();

foreach (var kvp in rawData)
{
    //var dt = string.Format("{0}-{1}", kvp.Key.ToString("MMMM"), kvp.Key.Year);
    var dt = kvp.Key.ToString("MMMM-yyyy");   //simpler alternative, "MMMM" gives full month name
    foreach (var val in kvp.Value)
    {
        if (val.Key == ReportType.Open)
        {
            if (openCounts.ContainsKey(dt))
                openCounts[dt] += val.Value;
            else
                openCounts.Add(dt, val.Value);
        }
        else
        {
            if (sentCounts.ContainsKey(dt))
                sentCounts[dt] += val.Value;
            else
                sentCounts.Add(dt, val.Value);
        }
    }
}

答案 2 :(得分:1)

In the first part of your query, you are taking only first item in the collection. Then you are grouping which is wrong. Try below:

        var ob = (from m in rawData
                  where m.Value.Keys.Any(i => i != ReportType.Sent)
                  group m by new { m.Key.Year, m.Key.Month, m.Value.Keys } into gx
                  select new
                  {
                      Date = new DateTime(gx.Key.Year, gx.Key.Month, 1),
                      //                          taa = gx.Key.Keys,
                      Total = gx.Select(m => m.Value.Values).ToList().Select(y => y.Sum()).FirstOrDefault()
                  }).ToList();

答案 3 :(得分:1)

我使用下面的代码来实现您的期望:

var result = rawData
    .SelectMany(c => c.Value, (b, c) => new { b.Key.Year, Month = b.Key.ToString("MMMM"), c.Key, c.Value })
    // At first I unwind nested data
    .GroupBy(g => g.Key)
    // Then create my new group by ReportType
    .Select(c => new { 
        c.Key, 
        Data = c.GroupBy(g => new { g.Year, g.Month })
                .Select(f => new { f.Key.Year, f.Key.Month, Sum = f.Sum(s => s.Value) })
        // Each ReportType has many groups of monthes of each year
    })
    // At last create my custom type
    .ToList();

C# Fiddle Demo