比较LINQ中的列表并返回2个结果对象

时间:2019-04-24 18:16:10

标签: c# linq

我有2个列表:List<Car> newCarsList<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很陌生,我不确定上面使用的逻辑。

  1. 有人可以帮我一个更好,更优化的方式工作吗?

  2. 能否在一个查询中完成并返回2个结果集(作为元组)?

我意识到,使用等于或不等于上述代码运行相同的代码可能是一种昂贵的方法。

4 个答案:

答案 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)

结果是一个包含新车和一个布尔值Presenttrue的布尔值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