[英]Compare list in LINQ and return 2 result objects
I have 2 Lists: List<Car> newCars
and List<Car> oldCars
. 我有2个列表:
List<Car> newCars
和List<Car> oldCars
。
I need to use the List<Car> newCars
as the source of truth and return both Present and Absent cars in the List<Car>oldCars
list. 我需要使用
List<Car> newCars
作为事实来源,并在List<Car>oldCars
列表中返回当前和缺席汽车。
How can I do this? 我怎样才能做到这一点?
My approach: 我的方法:
var present = newCars.Where(c => oldCars.All(w => w.Id != c.Id));
var absent = newCars.Where(c => oldCars.All(w => w.Id == c.Id));
I am quite new to LINQ, and I am not sure of the logic that I have used above. 我对LINQ并不陌生,我不确定上面使用的逻辑。
Can someone help me out to get this working in a better and optimized way? 有人可以帮助我以更好和优化的方式工作吗?
Can this be done in one single query and return 2 result sets (as tuples)? 能否在一个查询中完成并返回2个结果集(作为元组)?
I realize that running the same code with equals and not equals as above can be a costly approach. 我意识到,使用上面的等于或不等于代码运行相同的代码可能是一种昂贵的方法。
You can create custom IEqualityComparer
: 您可以创建自定义
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();
}
}
And use it like this: 并像这样使用它:
var present = newCars.Intersect(oldCars, new CarEqualityComparer());
var absent = newCars.Except(oldCars, new CarEqualityComparer());
You can utilize an anonymous variable containing the two lists: 您可以利用包含两个列表的匿名变量:
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
};
When you are saying that new cars are the source of truth, that means you want them always in the list, but you may not have an equivalent old car. 当您说新车是真理的源头时,这意味着您希望它们始终在列表中,但您可能没有同等的旧车。 You are describing a left-join.
您正在描述左联接。
This should get you most of what you want. 这应该为您带来大部分所需的东西。
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)
The result is a tuple containing the new car and a boolean Present
that is true
or false
based on whether there is an old car or not. 结果是一个元组,其中包含新车和一个布尔值
Present
,根据是否有旧车,该布尔值为true
或false
。
That gives you a single result set. 这样就可以得到一个结果集。 You wanted two result sets as a tuple.
您想要两个结果集作为元组。 To get this, just extend the above query, grouping by
Present
. 为此,只需扩展以上查询(按
Present
分组)即可。 This gives you a result set with exactly two items 这为您提供了一个包含两个项目的结果集
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();
from which you can get a single tuple containing both lists 从中您可以得到一个包含两个列表的元组
var finalResult = (Present: results[0], Absent: results[1]);
Using an extension method to treat adding to a list (a tiny bit) more functionally, you can use LINQ Aggregate
to do this in one pass: 使用扩展方法可以更功能地处理添加到列表(一点点),可以使用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)));
Where AfterAdd
is defined in a static class as: 在静态类
AfterAdd
定义为:
public static class ListExt {
public static List<T> AfterAdd<T>(this List<T> head, params T[] tail) {
head.AddRange(tail);
return head;
}
}
But it is much clearer to just use a foreach
loop: 但是,仅使用一个
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);
}
In both cases, creating the HashSet
ensures you don't have to scan through the oldCars
list to find each newCar
. 在这两种情况下,创建
HashSet
可以确保您不必浏览oldCars
列表即可找到每个newCar
。 If you knew the lists were ordered by Id
, you could do a more complicated one-pass solution traveling down the lists in parallel, but that hardly seems worth the complexity even then. 如果您知道列表是由
Id
排序的,则可以执行一个更复杂的单遍解决方案,以并行方式沿列表向下移动,但是即使那样,这似乎也不值得考虑其复杂性。
NOTE: A Join
effectively does something similar, converting the second List
into a Lookup
(like a Dictionary
) that can be used to find the matches with the first List
. 注意:
Join
有效地执行了类似的操作,将第二个List
转换为一个Lookup
(如Dictionary
),可用于查找与第一个List
的匹配项。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.