代码之家  ›  专栏  ›  技术社区  ›  Chris Ballance

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

  •  0
  • Chris Ballance  · 技术社区  · 12 年前

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

    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从数据库中提取),并删除当前在 this.Observations ,这是一个 ICollection<Observations>

    我尝试过使用.Except(this.Observations),但得到一个错误,我认为这可能与在IEnumerable实体上使用Except和ICollection有关。

    任何将删除 foreach 循环将是一个好的开始。

    2 回复  |  直到 12 年前
        1
  •  2
  •   Chris Ballance    12 年前

    你的循环相当于:

    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));
    

    这样,您只需遍历主列表一次,即可创建 HashSet 可以在O(1)时间内搜索。

        2
  •  1
  •   khellang    12 年前

    您可以在此处执行两个优化:

    1. 限制从数据库获取的观察值的数量
    2. 更快地查找所选观察ID

    当前实施具有 O(N * M) 复杂性,其中 N 是中的项目数 db.Observations 和 M 是中的项目数 this.Observations .

    首先创建一个 HashSet 中的ID 这。观察 :

    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));
        }
    }