【问题标题】:Compare Two Lists Via One Property Using LINQ使用 LINQ 通过一个属性比较两个列表
【发布时间】:2013-06-23 19:30:08
【问题描述】:

假设我有以下内容:

    class Widget1{
        public int TypeID { get; set; }
        public string Color { get; set; }
    }

    class Widget2
    {
        public int TypeID { get; set; }
        public string Brand { get; set; }
    }

    private void test()
    {
        List<Widget1> widgets1 = new List<Widget1>();
        List<Widget2> widgets2 = new List<Widget2>();
        List<Widget1> widgets1_in_widgets2 = new List<Widget1>();

        //some code here to populate widgets1 and widgets2

        foreach (Widget1 w1 in widgets1)
        {
            foreach (Widget2 w2 in widgets2)
            {
                if (w1.TypeID == w2.TypeID)
                {
                    widgets1_in_widgets2.Add(w1);
                }
            }
        }
    }

我使用两个 foreach 循环按 TypeID 比较列表以填充第三个列表。 有没有其他方法使用 LINQ 通过 TypeID 比较这两个列表?也许使用 Intersect 或其他一些功能?

【问题讨论】:

    标签: c# linq list collections


    【解决方案1】:

    你想要的是Join

    var widgets1_in_widgets2 = from first in widgest1
        join second in widgets2
        on first.TypeID equals second.TypeID
        select first;
    

    Intersect 可以或多或少地被认为是Join 的一个特例,其中两个序列属于同一类型,因此可以应用于相等而不需要为每种类型进行投影来生成密钥比较。鉴于您的情况,Intersect 不是一个选项。

    如果特定 ID 在您的第二组中重复,并且您不希望该项目在结果中重复,那么您可以使用 GroupJoin 而不是 Join

    var widgets1_in_widgets2 = from first in widgest1
        join second in widgets2
        on first.TypeID equals second.TypeID
        into matches
        where matches.Any()
        select first;
    

    【讨论】:

    • +1 " 鉴于您的情况,相交不是一个选项" 当自定义 IEqualityComparer&lt;BaseWidget&gt; 成为可能时,这将是一个选项,因此是一个常见的基本类型。然后您可以将其传递给this overload 或覆盖BaseWidget 中的GetHashCode 以默认通过TypeID 进行比较。
    【解决方案2】:

    Join 的缺点是,如果 widgets1 或 widgets2 包含多个具有相同 TypeID 的元素(顺便说一句,这也适用于您的原始代码),您的结果可能会重复。

    以下内容将完全满足您的要求:返回 widgets1 中的所有元素,其中在 widgets2 中存在具有相应 TypeID 的元素。

    widgets1_in_widgets2 = (from w1 in widgets1
                            where widgets2.Any(w2 => w1.TypeID == w2.TypeID)
                            select w1).ToList()
    

    【讨论】:

    • 虽然这会生成正确的输出,但它的性能会很差,因为您正在对另一个集合中的每个项目的集合进行线性搜索。
    • "Join has the drawback that your results might be duplicated if widgets1 or widgets2 contains elements with the same TypeID more than one." OP 中的代码实际上将具有与Join 相同的exact 输出,因此要么他在任一列表中都没有重复键,或者他想要重复的结果。
    • @Servy:好点,我会在回答中提到这一点。尽管如此,变量命名让我认为这是无意的。
    • 如果这是您想要解决的问题,那么您可以简单地使用GroupJoin,它为您提供了您所描述的解决方案,而无需您的答案相当高的性能成本。另请注意,虽然我同意变量命名表明他不希望结果中有重复项,但他可能在第二个列表中没有重复项,因此无需删除它们。
    【解决方案3】:

    你可以这样做

    widgets2.Where(y=>widget1.Any(z=>z.TypeID==y.TypeID));
    

    【讨论】:

    • 虽然这将生成正确的输出,但它的性能会相当差,因为您正在对另一个集合中的每个项目的集合进行线性搜索。
    • 最简单的更改是将其中一组的 TypeID 存储在 HashSet 中,这样可以更快地搜索。正如我所展示的,一个更重要的变化是只使用Join,尽管它最终会在引擎盖下做几乎相同的事情。
    • 很好的查询,在小列表中使用应该不是问题。
    • 又好又快的一个
    【解决方案4】:

    尝试使用“Where”的重载

    var isMatch = !widgets1.Where((w1, index) => w1.TypeId == widgets2[index].TypeId)).Any();
    

    【讨论】:

    • 一个只有代码没有解释的答案,尤其是当这个问题之前已经被彻底回答过时
    • 您可以更新此答案以指定它的不同之处。如果您想比较顺序并避免任何线性搜索,我发现这是一个有用的建议解决方案。
    【解决方案5】:

    我喜欢这个解决方案,因为它在代码中很容易阅读。

    bool result = firstList.All(o => secondList.Any(w => w.Prop1 == o.Prop1 && w.Prop2 == o.Prop2));
    

    查看 fiddle 中的完整示例:Fiddle example comparation

    【讨论】:

    • 谢谢,这对我有用。 if (projectIds.All(y => r.ProjectsId.Any(z => z.Equals(y)))) { reports.Add(r); }
    • 如果两个列表不同怎么办?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2019-09-01
    • 1970-01-01
    相关资源
    最近更新 更多