C# 如何在linq中加入连续的日期范围
我有这些数据示例C# 如何在linq中加入连续的日期范围,c#,.net,entity-framework,linq,lambda,C#,.net,Entity Framework,Linq,Lambda,我有这些数据示例 projects startdate enddate prj A 01 Jan 2019 10 Feb 2019 prj B 29 Jan 2019 15 Mar 2019 prj C 21 Mar 2019 02 May 2019 prj D 07 May 2019 10 Jun 2019 prj E 11 Jun 2019 30 Jul 2019 我想使用linq lambda使列表对象中的每个项都包含
projects startdate enddate
prj A 01 Jan 2019 10 Feb 2019
prj B 29 Jan 2019 15 Mar 2019
prj C 21 Mar 2019 02 May 2019
prj D 07 May 2019 10 Jun 2019
prj E 11 Jun 2019 30 Jul 2019
我想使用linq lambda使列表对象中的每个项都包含连续的项目列表
分组结果大致如下
[
[prj A, prj B],
[prj C],
[prj D, prj E]
]
- prj A和B分组是因为日期范围prj A与prj重叠 B
- 由于enddate prj B和startdate prj C之间有空格,所以 prj C属于不同的组
- prj D和E分组是因为起始日期 prj E是从enddate prj D开始的下一天
谢谢您不能通过Linq
GroupBy
它们,因为组中的每个项目在其他项目中可能没有公共分组键,或者找不到它,相反,两个邻居之间只存在一个公共键
var result=newlist();
var项目=新列表
{
新项目{Name=“prj A”,
startdate=新日期时间(2019,1,1),enddate=新日期时间(2019,2,10)},
新项目{Name=“prj B”,
startdate=新日期时间(2019,1,29),enddate=新日期时间(2019,3,15)},
新项目{Name=“prj C”,
startdate=新日期时间(2019,3,21),enddate=新日期时间(2019,5,2)},
新项目{Name=“prj D”,
startdate=新日期时间(2019,5,7),enddate=新日期时间(2019,6,10)},
新项目{Name=“prj E”,
startdate=新日期时间(2019,6,11),enddate=新日期时间(2019,7,30)}
};
projects=projects.OrderBy(x=>x.startdate.ToList();
对于(var i=0;i
您可以编写一个遵循LINQ风格的扩展方法JoinBy
充当分析滞后函数。它需要一个orderBy
和keySelector
来确定要计算的相邻值。join
用于决定何时可以连接相邻值
public static class LinqExtension
{
public static IEnumerable<IEnumerable<TSource>> JoinBy<TSource, TOrderKey, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TOrderKey> orderBy,
Func<TSource, TKey> keySelector,
Func<TKey, TKey, bool> join)
{
var results = new List<List<TSource>>();
var orderedSource = new List<TSource>(source).OrderBy(orderBy).ToArray();
if (orderedSource.Length > 0)
{
var group = new List<TSource> { orderedSource[0] };
results.Add(group);
if (orderedSource.Length > 1)
{
for (int i = 1; i < orderedSource.Length; i++)
{
var lag = orderedSource[i - 1];
var current = orderedSource[i];
if (join(keySelector(lag), keySelector(current)))
{
group.Add(current);
}
else
{
group = new List<TSource> { current };
results.Add(group);
}
}
}
}
return results;
}
}
公共静态类LinqExtension
{
公共静态IEnumerable JoinBy(
这是一个数不清的来源,
Func orderBy,
Func键选择器,
Func(连接)
{
var results=新列表();
var orderedSource=新列表(源).OrderBy(OrderBy.ToArray();
如果(orderedSource.Length>0)
{
var group=新列表{orderedSource[0]};
结果:添加(组);
如果(orderedSource.Length>1)
{
for(int i=1;i
…并使用它将相邻的项目组合在一起
public class Project
{
public string Name { get; set; }
public DateTime Begin { get; set; }
public DateTime End { get; set; }
}
[TestMethod]
public void TestCase1()
{
var projects = new List<Project>() {
new Project { Name = "A", Begin = new DateTime(2000, 1, 1), End = new DateTime(2000, 12, 31) }
, new Project { Name = "B", Begin = new DateTime(2001, 1, 1), End = new DateTime(2001, 12, 31) }
, new Project { Name = "C", Begin = new DateTime(2010, 1, 1), End = new DateTime(2010, 12, 31) }
, new Project { Name = "D", Begin = new DateTime(2010, 6, 1), End = new DateTime(2010, 7, 1) }
};
var grouped = projects.JoinBy(
x => x.Begin,
x => (begin: x.Begin, end: x.End),
(x1, x2) => x2.begin <= x1.end.AddDays(1) && x1.begin <= x2.end.AddDays(1));
var builder = new StringBuilder();
foreach (var grp in grouped)
{
builder.AppendLine(string.Join(", ", grp.Select(x => x.Name)));
}
var rendered = builder.ToString();
// rendered =>
// A, B
// C, D
}
公共类项目
{
公共字符串名称{get;set;}
公共日期时间开始{get;set;}
公共日期时间结束{get;set;}
}
[测试方法]
公共无效测试用例1()
{
var projects=新列表(){
新项目{Name=“A”,Begin=newdatetime(2000,1,1),End=newdatetime(2000,12,31)}
,新项目{Name=“B”,开始=新日期时间(2001,1,1),结束=新日期时间(2001,12,31)}
,新项目{Name=“C”,开始=新日期时间(2010,1,1),结束=新日期时间(2010,12,31)}
,新项目{Name=“D”,开始=新日期时间(2010,6,1),结束=新日期时间(2010,7,1)}
};
var group=projects.JoinBy(
x=>x.开始,
x=>(开始:x.开始,结束:x.结束),
(x1,x2)=>x2
//A,B
//C,D
}
编辑:来自注释的另一个测试用例
[TestMethod]
public void TestCase2_FromComments()
{
var projects = new List<Project>() {
new Project { Name = "A", Begin = new DateTime(2019, 4, 2), End = new DateTime(2019, 8, 17) }
, new Project { Name = "B", Begin = new DateTime(2019, 6, 1), End = new DateTime(2019, 7, 1) }
};
var grouped = projects.JoinBy(
x => x.Begin,
x => (begin: x.Begin, end: x.End),
(x1, x2) => x2.begin <= x1.end.AddDays(1) && x1.begin <= x2.end.AddDays(1));
var builder = new StringBuilder();
foreach (var grp in grouped)
{
builder.AppendLine(string.Join(", ", grp.Select(x => x.Name)));
}
var rendered = builder.ToString();
// rendered =>
// A, B
}
[TestMethod]
public void TestCase2_FromComments()
{
var projects=新列表(){
新项目{Name=“A”,开始=新日期时间(2019,4,2),结束=新日期时间(2019,8,17)}
,新项目{Name=“B”,开始=新日期时间(2019,6,1),结束=新日期时间(2019,7,1)}
};
var group=projects.JoinBy(
x=>x.开始,
x=>(开始:x.开始,结束:x.结束),
(x1,x2)=>x2
//A,B
}
使用APL扫描运算符的变体(如聚合
,仅返回中间结果)处理项目对,您可以创建一个GroupBy
,该运算符对项目对使用谓词lambda并创建组,只要其值为真:
public static class IenumerableExt {
// TKey combineFn((TKey Key, T Value) prevKeyItem, T curItem)
// prevKeyItem.Key = Previous Key (initially, seedKey)
// prevKeyItem.Value = Previous Item
// curItem = Current Item
// returns TKey for Current Item
public static IEnumerable<(TKey Key, T Value)> ScanToPairs<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
using (var srce = src.GetEnumerator())
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combineFn(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
// bool testFn(T prevVal, T curVal)
public static IEnumerable<IGrouping<int, T>> GroupPairsByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
src.ScanToPairs(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value);
}
公共静态类IenumerableExt{
//TKey组合Fn((TKey,T值)prevKeyItem,T curItem)
//prevKeyItem.Key=上一个键(最初为种子键)
//prevKeyItem.Value=上一项
//curItem=当前项目
//返回当前项的TKey
公共静态IEnumerable ScanToPairs(此IEnumerable src、TKey seedKey、Func组合){
使用(var srce=src.GetEnumerator())
if(srce.MoveNext()){
var prevkv=(种子键,srce.电流);
while(srce.MoveNext()){
收益率,千伏;
prevkv=(组合Fn(prevkv,srce.电流),srce.电流);
}
收益率,千伏;
}
}
//bool testFn(T preval,T curVal)
公共静态IEnumerable GroupPairsByWhile(此IEnumerable src,Func testFn)=>
src.ScanToPairs(1,(kvp,cur)=>testFn(kvp.Value,cur)?kvp.Key:kvp.Key+1)
.GroupBy(kvp=>kvp.Key,kvp=>kvp.Value)
public static class IenumerableExt {
// TKey combineFn((TKey Key, T Value) prevKeyItem, T curItem)
// prevKeyItem.Key = Previous Key (initially, seedKey)
// prevKeyItem.Value = Previous Item
// curItem = Current Item
// returns TKey for Current Item
public static IEnumerable<(TKey Key, T Value)> ScanToPairs<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
using (var srce = src.GetEnumerator())
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combineFn(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
// bool testFn(T prevVal, T curVal)
public static IEnumerable<IGrouping<int, T>> GroupPairsByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
src.ScanToPairs(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value);
}
var ans = projects.GroupPairsByWhile((prev,cur) => cur.startdate <= prev.enddate.AddDays(1));