使用LINQ选择集合中每个项目的列表,除非该项目存在于另一个集合中

本文关键字:项目 集合 另一个 存在 选择 LINQ 使用 列表 | 更新日期: 2023-09-27 17:50:03

这是可行的,但不仅看起来很糟糕,而且似乎效率也不高(我还没有评估性能,因为我知道一定有更好的方法)。

public IEnumerable<Observation> AvailableObservations
{
    get 
    {
        foreach (var observation in db.Observations)
        {
            if (Observations.Any(x => x.Id == observation.Id))
            {
            }
            else
            {
                yield return observation;
            }
        }
    }
}

本质上,我想要列表db.Observations中的所有内容(通过EF6从db中提取)并删除当前在this.Observations中选择的所有条目,这是ICollection<Observations>

我已经尝试使用。except (this.Observations),但得到一个错误,我认为可能与使用except与IEnumerable实体上的iccollection有关。

任何可以去除foreach循环的东西都是一个好的开始。

使用LINQ选择集合中每个项目的列表,除非该项目存在于另一个集合中

你的循环相当于:

return db.Observations.Where(o => !Observations.Any(oo => oo.Id == o.Id));

但这并不比你现有的效率高。

一个更有效的方法是创建一个id的HashSet并从中过滤:

HashSet<int> ids = new HashSet<int>(Observations.Select(o => o.Id));
return db.Observations.Where(o => !ids.Contains(o.Id));

这样,你只遍历主列表一次,以创建一个可以在O(1)时间内搜索的HashSet

这里可以做两个优化:

  1. 限制从数据库中获取的观测值的数量
  2. 使所选观测id的查找更快

当前实现的复杂度为O(N * M),其中Ndb.Observations中的项数,Mthis.Observations中的项数。

首先创建this.Observations:

中的id的HashSet,这将有助于提高性能。
var observationIds = new HashSet<int>(this.Observations.Select(x => x.Id));

这将允许您快速查找id。

与where子句(使用LINQ的Where())结合使用可以得到一个有效的查询:

public IEnumerable<Observation> AvailableObservations
{
    get 
    {
        var observationIds = new HashSet<int>(this.Observations.Select(x => x.Id));
        return db.Observations.Where(x => !observationIds.Contains(x.Id));
    }
}