在下面的linq查询中,我想添加一个获取前一个Department
以及当前Department
的属性。
下面的linq查询返回:
EffectiveDate EmployeeID Department
11/4/2012 10 0000
1/14/2013 10 9121
2/2/2016 10 9123
如何显示当前Department
旁边的上一个Department
?
EffectiveDate EmployeeID Department PreviousDepartment
11/4/2012 10 0000 null
1/14/2013 10 9121 0000
2/2/2016 10 9123 9121
这是当前查询
var users = from s in userTable
where s.EmployeeID == "10"
group new {s} by new { s.EmployeeID, s.Department} into g
select new
{
EffectiveDate = g.Max(m => m.s.EffectiveDate),
EmployeeID = g.Key.EmployeeID,
Department = g.Key.Department
//PreviousDepartment = ???
};
感谢您的帮助
答案 0 :(得分:1)
您可以使用LINQ中的Zip
方法生成当前和上一对。一个缺点是,它确实枚举了两次输入(如果它来自数据库,可能会出现问题),但另一方面,它并不能将整个输入保存在内存中,而是它的输入懒惰(即你不必全部消费)。
例如,使用int值作为演示:
[TestMethod]
public void CurrentAndPrevious()
{
var input = new int[] { 1, 2, 3, 4, 5 };
var output = Enumerable.Repeat(0,1) // an initial zero value
.Concat(input) // followed by the list
// zipped with the list
.Zip(input, (x, y) => new {current = y, previous = x});
// a test that passes (using FluentAssertions syntax)
string.Join(",", output.Select(x => $"({x.current},{x.previous})"))
.Should().Be("(1,0),(2,1),(3,2),(4,3),(5,4)");
}
答案 1 :(得分:0)
一种方法是将数据存储在内存中,然后像这样修改它:
N2
请注意,代码正在生成var users = from s in userTable
where s.EmployeeID == "10"
group new {s} by new { s.EmployeeID, s.Department} into g
select new MyClass
{
EffectiveDate = g.Max(m => m.s.EffectiveDate),
EmployeeID = g.Key.EmployeeID,
Department = g.Key.Department
PreviousDepartment = null
};
var result = users.ToList();
for(int i = 1; i < result.Count; i++)
{
result[i].PreviousDepartment = result[i-1].Department;
}
的新实例,而不是匿名类型,因为匿名类型属性是只读的。确保使用正确的属性创建此类。
答案 2 :(得分:0)
如果您使用Interactive Extensions(NuGet包Ix-Main),您可以执行以下操作:
var users =
userTable
.Where(x => x.EmployeeID == "100")
.GroupBy(x => new { x.EmployeeID, x.Department })
.Select(x => new
{
EffectiveDate = x.Max(m => m.EffectiveDate),
EmployeeID = x.Key.EmployeeID,
Department = x.Key.Department
})
.OrderBy(x => x.EffectiveDate)
.AsEnumerable()
.Scan(
new
{
EffectiveDate = default(DateTime),
EmployeeID = default(string),
Department = default(string),
PreviousDepartment = default(string)
},
(a, x) => new
{
EffectiveDate = x.EffectiveDate,
EmployeeID = x.EmployeeID,
Department = x.Department,
PreviousDepartment = a.Department
});
好处是你坚持使用声明性编程风格。缺点是它比对内存中的数据进行操作更冗长,更难阅读。哦,您可能需要安装NuGet包。对我来说这不是一个问题,因为我不认为我在没有添加Ix-Main的情况下开始任何项目。
Ix-Main,没有它就永远不会离开家。
答案 3 :(得分:0)
实现它的方法语法。
var result = userTable.Select((x, i) => { Department j = null ; if (i>0) j = userTable.ElementAt(i-1).Department; return new { x.EffectiveDate , x.EmployeeID , x.Department , j};});
答案 4 :(得分:0)
大多数其他答案都强制 IEnumerable
被完全迭代,在某些情况下是两次。 无需任何额外迭代就可以做到这一点,换句话说:与LINQ 迭代流程。
public static IEnumerable<(T current, T previous)> WithLag<T>(this IEnumerable<T> source)
{
var previous = default(T);
var hasPrev = false;
foreach (var item in source)
{
if(hasPrevious)
yield return (item, previous);
else
yield return (item, default(T));
hasPrev = true;
previous = item;
}
}
然后您可以以正常的 LINQ 方式使用它(您需要方法语法):
var users = userTable
.Where(s => s.EmployeeID == "10")
.GroupBy(s => new { s.EmployeeID, s.Department})
.WithLag()
.Select(gLag => new
{
EffectiveDate = gLag.current.Max(m => m.s.EffectiveDate),
EmployeeID = gLag.current.Key.EmployeeID,
Department = gLag.current.Key.Department
PreviousDepartment = gLag.previous.Key.Department
});