我有一个IEnumerable<T>
,它有一个Created
字段,这是一个日期。
每个日期可以有多个T
,有时在给定日期没有T
。
目前我按日期分组,这给了我至少有一个T
的所有日期,以及它们下面的T
。
我想要的是,我可以使用的东西作为查询的一部分,将得到我的所有日期在一个范围内,不管是否有任何T
's与给定的日期。
var adjustments = DAL.GetAdjustmentsInDateRange(Start, End);
from adjustment in adjustments
group adjustment by adjustment.Created.Date into adjustmentsByDay
orderby adjustmentsByDay.Key descending
select ....
这里, adjustmentsByDay
没有Start
和End
之间的所有日期。我想要的是包含它们,而不是元素。
我该怎么做呢?
您可以在分组之前使用所有日期列表进行连接调整,如下所示:
var adjustments = DAL.GetAdjustmentsInDateRange(Start, End);
// Get all unique dates in time span
IEnumerable<DateTime> dates = GetAllDates(Start, End);
var query = (from date in dates
join adjustment in adjustments
on date.Date equals adjustment.Created.Date into a
from adjustment in a.DefaultIfEmpty()
select new {date.Date, adjustment}
).GroupBy(i=>i.Date).OrderBy(g=>g.Key);
我已经组合了一个通用的LINQ-to-objects扩展方法来将缺失的东西插入到序列中:
public static IEnumerable<T> InsertMissing<T, U>(this IEnumerable<T> source,
Func<T, U> key, Func<U, U> increment, Func<U, T> create)
{
bool first = true;
U last = default(U);
foreach (var ig in source)
{
U current = key(ig);
if (first)
{
first = false;
last = current;
yield return ig;
continue;
}
while (!(last = increment(last)).Equals(current))
{
yield return create(last);
}
yield return ig;
}
}
您还需要自定义实现IGrouping
:
class EmptyGrouping<K, E> : IGrouping<K, E> {
public K Key { get; set; }
public IEnumerator<E> GetEnumerator() {
return Enumerable.Empty<E>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
}
然后你需要在orderby
之后结束你的查询,然后用这个调用,然后把你的select
放在后面:
var allGroups = query.InsertMissing(
// Key selector
g => g.Key,
// Get next desired key from current key
d => d.AddDays(-1),
// Create item for missing key
d => new EmptyGrouping<DateTime,YourAdjustmentType>{ Key = d });
这个将乱套,如果你的钥匙没有顺序,或者如果其中一个没有落在正确的位置(例如,在你的情况下,不是在午夜)。
这样做的优点是不需要在原始源上进行多次查询来确定最小/最大值以生成键列表,然后再进行进一步查询以连接并获取数据。