简体   繁体   English

基于日期实体框架和LINQ的数据分组

[英]Grouping data based on date entity framework and LINQ

I have a array of analytic events in my database and i would like to send this data grouped by date to my client app.我的数据库中有一系列分析事件,我想将此按日期分组的数据发送到我的客户端应用程序。

The data from the db looks something like this (but with hundreds of records):来自数据库的数据看起来像这样(但有数百条记录):

[
  {
    "DateAdded": "2006-12-30 00:38:54",
    "Event": "click",
    "Category": "externalWebsite"
  },
  {
    "DateAdded": "2006-07-20 00:36:44",
    "Event": "click",
    "Category": "social"
  },
  {
    "DateAdded": "2006-09-20 00:36:44",
    "Event": "click",
    "Category": "social"
  },
  {
    "DateAdded": "2006-09-22 00:12:34",
    "Event": "load",
    "Category": "profile"
  }
]

What I would like to do is return the count of all the say 'social' 'click' but by month so it would look like this:我想要做的是返回所有说“社交”“点击”的计数,但按月返回它看起来像这样:

[
  {
    "name": "socialclicks",
    "series": [
      {
        "count": 259,
        "name": "Jan"
      },
      {
        "count": 0,
        "name": "Feb"
      },
      {
        "count": 52,
        "name": "Mar"
      }
      ... etc, etc up to Dec <====
    ]
  }
]

So, what I have been trying is to get all the records that are associated with a particular user using their id.因此,我一直在尝试使用特定用户的 id 获取与特定用户关联的所有记录。 This is simple.这很简单。

Now I need to split them records into monthly counts showing the last 12 months from the current month (if the month doesn't exist return 0) - this is proving to be complicated and difficult.现在我需要将它们的记录拆分为显示从当月开始的最后 12 个月的月度计数(如果该月不存在,则返回 0) - 这被证明是复杂和困难的。

My approach was this:我的方法是这样的:

var records = context.records.where(r => r.Id = userId).ToList();
var jan
var feb
var mar
var apr
... etc, etc

for (int i = 0; i < records.Count ; i++)
{
    if (record.DateAdded > "2005-12-31 00:00.00" && record.DateAdded < "2006-01-31 00:00.00") {
       jan++;
    }

    if (record.DateAdded > "2006-01-31 00:00.00" && record.DateAdded < "2006-02-28 00:00.00") {
       feb++;
    }

    ...etc, etc
}

Then i use these variables to count and hard code the name for the returned data.然后我使用这些变量来计算和硬编码返回数据的名称。

As you can see, there is lots of etc, etc because the code has become ridiculous!正如你所看到的,有很多等等,因为代码变得荒谬了!

There must be a more simple way to do this but i cant seem to find one!必须有一种更简单的方法来做到这一点,但我似乎找不到一个!

Any assistance would be appreciated.任何援助将不胜感激。

Thanks谢谢

The first thing to do is group all your data by the 2 properties you're interested in首先要做的是按您感兴趣的 2 个属性对所有数据进行分组

  • Event事件
  • Category类别

Example:例子:

 var partialResult = entries.GroupBy(x => new {
       x.Event,
       x.Category
    });

From there, when you project your result and you can group again by Month & Year.从那里,当您预测结果时,您可以按月和年再次分组。 - anonymous object used for demo, but you could easily define this as a struct/class as appropriate: - 用于演示的匿名对象,但您可以根据需要轻松地将其定义为结构/类:

var result = entries.GroupBy(x => new {
       x.Event,
       x.Category
    }).Select(g => new {
       g.Key.Event,
       g.Key.Category,
       Series = g.GroupBy(x => new {x.DateAdded.Month, x.DateAdded.Year})
                 .Select(i => new{
                         i.Key.Month,
                         i.Key.Year,
                         Count = i.Count()
                }).ToArray()
    });

foreach(var item in result)
{
    Console.WriteLine($"Event:{item.Event} Category:{item.Category}");
    foreach(var serie in item.Series)
        Console.WriteLine($"\t{CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(serie.Month)}{serie.Year} Count={serie.Count}");
}

Edit: To satisfy your requirement that:编辑:为了满足您的要求:

if the month doesn't exist return 0如果月份不存在返回0

You need to add a few complexities.您需要添加一些复杂性。 First a method which can work out all the Month/Year combinations between 2 dates.首先是一种可以计算出 2 个日期之间的所有月/年组合的方法。

private static IEnumerable<(int Month, int Year)> MonthsBetween(
        DateTime startDate,
        DateTime endDate)
{
    DateTime iterator;
    DateTime limit;

    if (endDate > startDate)
    {
        iterator = new DateTime(startDate.Year, startDate.Month, 1);
        limit = endDate;
    }
    else
    {
        iterator = new DateTime(endDate.Year, endDate.Month, 1);
        limit = startDate;
    }

    var dateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat;
    while (iterator < limit)
    {
        yield return (iterator.Month,iterator.Year);                
        iterator = iterator.AddMonths(1);
    }
}

Also you'll need some kind of range to both calculate all the months between, as well as filter your original query:此外,您还需要某种范围来计算之间的所有月份,以及过滤您的原始查询:

var dateRangeStart = DateTime.Parse("2006-01-01");
var dateRangeEnd = DateTime.Parse("2007-01-01");
var monthRange = MonthsBetween(dateRangeStart,dateRangeEnd);

var results = entries.Where(e => e.DateAdded>=dateRangeStart && e.DateAdded<dateRangeEnd)
     ..... etc

And then, when outputting results you need to effectively do a left join onto your list of years/months.然后,在输出结果时,您需要有效地对年/月列表进行左连接。 For some reason this is easier using query syntax than lambda.出于某种原因,使用查询语法比使用 lambda 更容易。

foreach(var item in results)
{
    Console.WriteLine($"Event:{item.Event} Category:{item.Category}");

    var joinedSeries = from month in monthRange
                        join serie in item.Series
                        on new{month.Year, month.Month} equals new {serie.Year, serie.Month} into joined
                        from data in joined.DefaultIfEmpty()
                        select new {
                            Month = data == null ? month.Month : data.Month,
                            Year = data == null ? month.Year : data.Year,
                            Count = data == null ? 0 : data.Count
                        };


foreach(var serie in joinedSeries)
    Console.WriteLine($"\t{CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(serie.Month)}{serie.Year} Count={serie.Count}");
}

Live example: https://dotnetfiddle.net/K7ZoJN实例: https : //dotnetfiddle.net/K7ZoJN

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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