我有2个列表:List<Car> newCars
和List<Car> oldCars
。
我需要使用List<Car> newCars
作为真相来源,并返回List<Car>oldCars
列表中的当前和缺席汽车。
我该怎么做?
我的方法:
var present = newCars.Where(c => oldCars.All(w => w.Id != c.Id));
var absent = newCars.Where(c => oldCars.All(w => w.Id == c.Id));
我对LINQ很陌生,我不确定上面使用的逻辑。
有人可以帮我一个更好,更优化的方式工作吗?
能否在一个查询中完成并返回2个结果集(作为元组)?
我意识到,使用等于或不等于上述代码运行相同的代码可能是一种昂贵的方法。
答案 0 :(得分:0)
您可以创建自定义IEqualityComparer
:
public class CarEqualityComparer : IEqualityComparer<Car>
{
public bool Equals(Car x, Car y)
{
return x.Id == y.Id;
}
public int GetHashCode(Car obj)
{
return obj.Id.GetHashCode();
}
}
并像这样使用它:
var present = newCars.Intersect(oldCars, new CarEqualityComparer());
var absent = newCars.Except(oldCars, new CarEqualityComparer());
答案 1 :(得分:0)
您可以使用包含两个列表的匿名变量:
var res = new
{
present = newCars.Where(n => oldCars.Select(o => o.Id).Contains(n.Id)), // present if the old cars list contains the id from the new list
absent = newCars.Where(n => !oldCars.Select(o => o.Id).Contains(n.Id)) // absent if the old cars list does not contain the id from the new list
};
答案 2 :(得分:0)
当您说新车是真理的源头时,这意味着您希望它们始终在列表中,但您可能没有同等的旧车。您正在描述左联接。
这应该为您带来大部分所需的东西。
var results = from n in newCars
join o in oldCars
on n.Id == o.Id
group into cars
from oc in cars.DefaultIfEmpty()
select (NewCar: n, Present: oc != null)
结果是一个包含新车和一个布尔值Present
或true
的布尔值false
的元组,该布尔值取决于是否有旧车。
这将为您提供一个结果集。您想要两个结果集作为元组。为此,只需扩展以上查询,并按Present
分组即可。这为您提供了包含两个项目的结果集
var results = (from r in (from n in newCars
join o in oldCars
on n.Id equals o.Id
into cars
from oc in cars.DefaultIfEmpty()
select (NewCar: n, Present: oc != null))
group r by r.Present into g
orderby g.Key descending
select g.Select(x => x.NewCar).ToList()).ToList();
从中可以获得包含两个列表的单个元组
var finalResult = (Present: results[0], Absent: results[1]);
答案 3 :(得分:0)
使用扩展方法在功能上更多地对待添加到列表(一点点),您可以使用LINQ Aggregate
一次完成此操作:
var idsFromOldCars = oldCars.Select(c => c.Id).ToHashSet();
var(present, absent) = newCars.Aggregate((p:new List<Car>(),a:new List<Car>()), (pa,nc) => idsFromOldCars.Contains(nc.Id) ? (pa.p.AfterAdd(nc),pa.a) : (pa.p,pa.a.AfterAdd(nc)));
在静态类中将AfterAdd
定义为:
public static class ListExt {
public static List<T> AfterAdd<T>(this List<T> head, params T[] tail) {
head.AddRange(tail);
return head;
}
}
但是,仅使用foreach
循环就更清楚了:
var idsFromOldCars = oldCars.Select(c => c.Id).ToHashSet();
var present = new List<Car>();
var absent = new List<Car>();
foreach (var nc in newCars) {
if (idsFromOldCars.Contains(nc.Id))
present.Add(nc);
else
absent.Add(nc);
}
在两种情况下,创建HashSet
都可以确保您不必浏览oldCars
列表即可找到每个newCar
。如果您知道列表是按Id
排序的,则可以做一个更复杂的单遍解决方案,以并行方式沿列表行进,但即使那样,看起来也不值得这样做。
注意:Join
有效地执行了类似的操作,将第二个List
转换为Lookup
(例如Dictionary
),可以用来查找与首先List
。