Linq:按日期间隔分组(5,10,15等..年)
问题描述:
我有一个MVC/ASP.NET Web应用程序跟踪员工信息,如StartDate,这是一个DateTime数据类型,员工在公司工作。Linq:按日期间隔分组(5,10,15等..年)
我们还在公司为员工颁发时间奖,并希望在公司中分5,10,15,20,25,30年以上。
我该如何编写一个Linq声明,该声明将仅由在公司时间已达到年度间隔基准(5,10,15等等)中的员工(人员)进行分组, “时间在公司”奖?谢谢你的帮助。
答
查看以下用于演示如何按期间/间隔进行分组的单元测试。
[TestClass]
public class GroupByDateIntervalsTests {
[TestMethod]
public void Group_By_5_year_Intervals_Max_30() {
var employees = GenerateRandomDates(DateTime.Now, 5, 40, 50).Select((d, i) => new { id = i, StartDate = d });
var now = DateTime.Today;
var period = 5;
var maxPeriod = 30;
var groups = from employee in employees
let interval = DateTime.MinValue.AddDays((now - employee.StartDate).TotalDays).Year/period
group employee by Math.Min(interval * period, maxPeriod) into g
orderby g.Key
select new {
period = g.Key,
employees = g.Select(e => e.id).ToArray()
};
var result = groups.ToList();
}
[TestMethod]
public void Group_By_Random_Interval_Max_30() {
var employees = GenerateRandomDates(DateTime.Now, 5, 40, 50).Select((d, i) => new { id = i, StartDate = d });
var now = DateTime.Today;
var periods = new[] { 5, 10, 20, 30 };
var groups = employees
.GroupBy(employee => {
var period = DateTime.MinValue.AddDays((now - employee.StartDate).TotalDays).Year;
var interval = periods.Where(p => (period/p) > 0).Max();
return Math.Min(interval, periods.Max());
})
.Select(g => new {
period = g.Key,
employees = g.Select(e => e.id).ToArray()
});
var result = groups.ToList();
}
public List<DateTime> GenerateRandomDates(DateTime rootDate, int minAgeAtRootDate, int maxAgeAtRootDate, int count) {
Contract.Assert(minAgeAtRootDate <= maxAgeAtRootDate, "invalid age range. Minimum age cannot be higher than maximum age");
var minDate = rootDate.Date.AddYears(-maxAgeAtRootDate);
var maxDate = rootDate.Date.AddYears(-minAgeAtRootDate);
var range = (maxDate - minDate).Days;
if (range == 0) {
range = 364;
}
var random = new Random();
var dates = Enumerable
.Range(1, count)
.Select(i => minDate.AddDays(random.Next(range)))
.ToList();
return dates;
}
}
答
这个解决方案非常棒!通过您的解决方案,我掌握了您的主要逻辑,并进行了一些改变,我认为其他人可能从中受益。绝对不可能以你的耐心来帮助你,并且我的专业知识与我正在努力完成的事情无关。
请参阅以下我的变化...
控制器
public ActionResult LengthOfService()
{
ViewBag.Title = "Length of Service Awards Report";
var people = db.People.ToList().Where(p => !p.EndDate.HasValue);
var now = DateTime.Today;
var period = 5;
var maxPeriod = 30;
var query = (from p in people
let interval = DateTime.MinValue.AddDays((now - p.LengthOfService).TotalDays).Year/period
where interval > 0
group p by Math.Min(interval * period, maxPeriod) into x
orderby x.Key
select new AwardInfo
{
Years = x.Key,
People = x
}).ToList();
return View(query);
}
视图模型
public class AwardInfo
{
public int Years { get; set; }
public IEnumerable<People> People { get; set; }
}
查看
@model List<CPR.Models.AwardInfo>
<h2>@ViewBag.Title</h2>
<table class="table table-responsive table-hover">
<thead>
<tr>
<th class="col-sm-1">Service Award</th>
<th class="col-sm-2">Name</th>
<th class="col-sm-2">Date of Service</th>
<th class="col-sm-2">Company</th>
</tr>
</thead>
@foreach (var item in Model)
{
<tbody>
<tr>
<th class="panel-bg" colspan="5">@item.Years years</th>
</tr>
</tbody>
<tbody>
@foreach (var person in item.People)
{
<tr>
<td class="col-sm-1"></td>
<td class="col-sm-2">@person.LastName, @person.FirstName</td>
<td class="col-sm-2">@person.LengthOfService.ToShortDateString()</td>
<td class="col-sm-2">@person.Companies.Name</td>
</tr>
}
</tbody>
}
</table>
你需要证明你已经尝试一下,提供一些示例代码。你有没有搜索过答案,因为我确信之前有人问过这个问题? – DavidG
@DavidG - 我一直在找东西,并没有找到一个解决方案。我发现的那些是专门用于SQL(并且无法转换为linq),或者不适用于以前设置的静态时间间隔。我唯一的示例代码是查询数据上下文以获取在公司内部活动的人员(!EndDate.HasValue),以及由StartDate分组。我之前做过很多分组,之前没有这样的设置数据。 – Element808
@Nkosi - 遗憾的是,我的情况略有不同,因为我只想要一个专门针对那些适合那个年份的人的专属查询。不是所有的人。 – Element808