[英]LINQ group by date - include empty days WITHOUT using join
Using C#, NHibernate, NHibernate to LINQ. 使用C#,NHibernate,NHibernate到LINQ。 Using NHibernate to LINQ, I do not have the JOIN functionality.
使用NHibernate到LINQ,我没有JOIN功能。 I can not use QueryOver either.
我也不能使用QueryOver。
I have a LINQ query that counts the amount of Leads and Sales. 我有一个LINQ查询,它计算潜在客户和销售量。 This table only creates when a new lead or sale is done, so some days there is no row inserted.
该表仅在完成新的潜在客户或销售时创建,因此某些天没有插入任何行。 Doing the following query works
执行以下查询
var query3 = query2.AsEnumerable()
.Where(x => x.Created <= toDate.Value && x.Created >= fromDate.Value)
.GroupBy(x => x.Created.Date)
.Select(g => new ReferrerChart {
Date = g.Key.Date,
LeadsCount = g.Count(x => !x.IsSale),
SalesCount = g.Count(x => x.IsSale)
});
but some dates are not in there (the dates there were 0 leads and sales). 但是某些日期不在其中(日期有0个潜在顾客和销售)。
How can I include these dates and let them set LeadsCount = 0 and SalesCount = 0 WITHOUT using join? 如何包含这些日期,并允许它们不使用join设置LeadsCount = 0和SalesCount = 0而没有?
EDIT: 编辑:
Final result that works: 起作用的最终结果:
var selectedFromDate = fromDate.Value;
var selectedToDate = toDate.Value;
var selectedDates = Enumerable.Range(0, 1 + selectedToDate.Subtract(selectedFromDate).Days)
.Select(offset => new ReferrerChart {
Date = selectedFromDate.AddDays(offset),
LeadsCount = 0,
SalesCount = 0
})
.ToList();
var query3 = Find(x => x.Source == "UserRef").AsEnumerable()
.Where(x => x.Created <= toDate.Value && x.Created >= fromDate.Value)
.GroupBy(x => x.Created.Date)
.Select(g => new ReferrerChart {
Date = g.Key.Date,
LeadsCount = g.Count(x => !x.IsSale),
SalesCount = g.Count(x => x.IsSale)
}).ToList();
var result = query3.Union(
selectedDates
.Where(e => !query3.Select(x => x.Date).Contains(e.Date)))
.OrderBy(x => x.Date);
You can Union()
the data from your query with dummy items that you create in memory. 您可以将查询中的数据与您在内存中创建的虚拟项进行
Union()
。 For example: 例如:
var query4 = query3.ToList(); // Prevent multiple execution.
var startDate = DateTime.Today.AddDays(-99);
var emptyData = Enumerable.Range(1,100).Select (i =>
new ReferrerChart
{
Date = startDate.AddDays(i),
LeadsCount = 0,
SalesCount = 0
});
var result = query4 .Union(
emptyData
.Where(e => !query4.Select(x => x.Date).Contains(e.Date)))
.OrderBy(x => x.Date);
There are two things I would change in your LINQ Query, I'd include the null dates 我将在LINQ查询中更改两件事,包括空日期
.Where(x => x.Created <= toDate.Value && x.Created >= fromDate.Value)
becomes something like 变成像
.Where(x => x.Created.Date == null || (x.Created <= toDate.Value && x.Created >= fromDate.Value))
My other pointer would be that your .GroupBy requires a x.Created.Date, use of IsNull or similar function to set the value here to a value regardless of whether the entry is Null 我的另一个指针是您的.GroupBy需要一个x.Created.Date,使用IsNull或类似函数将此处的值设置为一个值,而不管条目是否为Null
Eg (x.Created == null ? "" : x.Created.ToString("yyyyMMdd"))
例如
(x.Created == null ? "" : x.Created.ToString("yyyyMMdd"))
(I apologize but I'm not on my development PC at this moment in time so can't definitively state the correct code) (我很抱歉,但是目前我不在开发PC上,因此无法确切说明正确的代码)
There's a great way to do it by implementing an extension method for IEnumerable<IGrouping<TKey, TElement>>
(the type that is returned from GroupBy
). 通过实现
IEnumerable<IGrouping<TKey, TElement>>
(从GroupBy
返回的类型)的扩展方法,有一种很棒的方法。 In my humble opinion it's the best approach for a number of reasons: 以我的拙见,出于多种原因,这是最好的方法:
.ToList()
also accomplishes that, but at the price of eager evaluation), .ToList()
也可以完成此操作,但要以急切的评估为代价), GroupBy
, GroupBy
惰性评估(延迟执行), public static IEnumerable<IGrouping<TKey, TElement>> Fill<TKey, TElement>(this IEnumerable<IGrouping<TKey, TElement>> groups, IEnumerable<TKey> filling)
{
List<TKey> keys = filling.ToList();
foreach (var g in groups)
{
if(keys.Contains(g.Key))
keys.Remove(g.Key);
yield return g;
}
foreach(var k in keys)
yield return new EmptyGrouping<TKey, TElement>(k);
}
class EmptyGrouping<TKey, TElement> : List<TElement>, IGrouping<TKey, TElement>
{
public TKey Key { get; set; }
public EmptyGrouping(TKey key)
{
this.Key = key;
}
}
Random rand = new Random();
var results = Enumerable.Repeat(0, 5) // Give us five
.Select(i => rand.Next(100)) // Random numbers 0 - 99
.GroupBy(r => r.Dump("Calculating group for:") / 10) // Group by tens (0, 10, 20, 30, 40...)
.Fill(Enumerable.Range(0, 10)) // Fill aby missing tens
.OrderBy(g => g.Key); // Sort
@"Anything above = eager evaluations.
Anything below = lazy evaluations.".Dump();
results.Dump();
(Five integers on top are printed from inside the query when it's being evaluated. As you can see, there was only one calculation pass). (在对查询进行评估时,从查询内部打印出五个整数。您可以看到,只有一次计算通过)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.