在 LINQ to Objects 中预过滤连接元素
Pre-filtering joined elements in LINQ to Objects
我有两个集合,'left' 和 'right',它们具有不同的元素类型。元素类型都有一个字符串 属性 'paramNumber' ,它不是唯一的,并且通过它的关系是多对多的。现在,有两个 DateTime 字段,'left.date' 和 'right.startDate'。对于每个选定的 'left.date',我需要获取 'right' 中的最后一个元素,其中 'right.startDate' 小于或等于(除了连接条件 'left.paramNumber' == 'right.paramNumber' ).
请问有没有不全交叉连接的直接方式?我用子查询尝试了它,但遇到了 'lefty'' 变量范围界定的问题(参见代码)。
我在网上看到教程先进行完全交叉连接,然后再删除 'where' 子句中不需要的行,但这对我们来说不是办法。
class left
{
public string paramNumber;
public DateTime date;
public string leftyName;
}
class right
{
public string paramNumber;
public DateTime startDate;
public string anotherString;
public DateTime timeOfDay;
}
class Program
{
static void Main(string[] args)
{
List<left> lefts = new List<left>();
List <right> rights = new List <right>();
//lefty not visible in the where clause...
//trying it with a second 'from right...' did not work because of
//casting problems.
var query =
from left lefty in lefts
join right righty in
((from right rightTmp in rights
where ((rightTmp.paramNumber == lefty.paramNumber) &&
(rightTmp.startDate <= lefty.date) &&
(rightTmp.anotherString == "N") )
select rightTmp
).ToList().Last())
on lefty.paramNumber equals righty.paramNumber
select new
{
myDate = lefty.date,
myLeftyName = lefty.leftyName,
myParamNumber = lefty.paramNumber,
myTimeOfDay = righty.timeOfDay
};
}
}
[编辑]:解决方案(基于cechode的回答):
下面是我目前使用的解决方案,基于cechode的回答。
'let' 子句是主要缺失的部分,因为它也允许引用 l/lefty,而不仅仅是 r/rightTmp.
最后,我对其中的一个所需元素进行了全面过滤
'rights' 直接在第一个 'let' 子句中(通过升序排序并使用
最后的();当然 cechode 的 descending/First() 也有效)并且省去了第二个
使用 'toprightcheck' 变量(我认为 cechode 有充分的理由进行空检查,但我会尝试使用较短的版本):
var X = (from l in lefts
let topright =
(from r in rights
where r.paramNumber == l.paramNumber &&
r.startDate <= l.date
orderby r.startDate ascending
select r).Last()
select new {
lName = l.paramNumber,
rname = topright.paramNumber,
ldate = l.date,
rdate = topright.startDate });
这对你有用吗?
var X = (from l in lefts
let topright = (from r in rights where r.paramNumber == l.paramNumber && r.startDate < l.date orderby r.startDate descending select r)
where topright!=null
let toprightcheck = topright.First()
select new { lName = l.paramNumber, rname = toprightcheck.paramNumber, ldate = l.date, rdate = toprightcheck.startDate });
我有两个集合,'left' 和 'right',它们具有不同的元素类型。元素类型都有一个字符串 属性 'paramNumber' ,它不是唯一的,并且通过它的关系是多对多的。现在,有两个 DateTime 字段,'left.date' 和 'right.startDate'。对于每个选定的 'left.date',我需要获取 'right' 中的最后一个元素,其中 'right.startDate' 小于或等于(除了连接条件 'left.paramNumber' == 'right.paramNumber' ).
请问有没有不全交叉连接的直接方式?我用子查询尝试了它,但遇到了 'lefty'' 变量范围界定的问题(参见代码)。
我在网上看到教程先进行完全交叉连接,然后再删除 'where' 子句中不需要的行,但这对我们来说不是办法。
class left
{
public string paramNumber;
public DateTime date;
public string leftyName;
}
class right
{
public string paramNumber;
public DateTime startDate;
public string anotherString;
public DateTime timeOfDay;
}
class Program
{
static void Main(string[] args)
{
List<left> lefts = new List<left>();
List <right> rights = new List <right>();
//lefty not visible in the where clause...
//trying it with a second 'from right...' did not work because of
//casting problems.
var query =
from left lefty in lefts
join right righty in
((from right rightTmp in rights
where ((rightTmp.paramNumber == lefty.paramNumber) &&
(rightTmp.startDate <= lefty.date) &&
(rightTmp.anotherString == "N") )
select rightTmp
).ToList().Last())
on lefty.paramNumber equals righty.paramNumber
select new
{
myDate = lefty.date,
myLeftyName = lefty.leftyName,
myParamNumber = lefty.paramNumber,
myTimeOfDay = righty.timeOfDay
};
}
}
[编辑]:解决方案(基于cechode的回答):
下面是我目前使用的解决方案,基于cechode的回答。 'let' 子句是主要缺失的部分,因为它也允许引用 l/lefty,而不仅仅是 r/rightTmp.
最后,我对其中的一个所需元素进行了全面过滤 'rights' 直接在第一个 'let' 子句中(通过升序排序并使用 最后的();当然 cechode 的 descending/First() 也有效)并且省去了第二个 使用 'toprightcheck' 变量(我认为 cechode 有充分的理由进行空检查,但我会尝试使用较短的版本):
var X = (from l in lefts
let topright =
(from r in rights
where r.paramNumber == l.paramNumber &&
r.startDate <= l.date
orderby r.startDate ascending
select r).Last()
select new {
lName = l.paramNumber,
rname = topright.paramNumber,
ldate = l.date,
rdate = topright.startDate });
这对你有用吗?
var X = (from l in lefts
let topright = (from r in rights where r.paramNumber == l.paramNumber && r.startDate < l.date orderby r.startDate descending select r)
where topright!=null
let toprightcheck = topright.First()
select new { lName = l.paramNumber, rname = toprightcheck.paramNumber, ldate = l.date, rdate = toprightcheck.startDate });