【问题标题】:how to compare every value in a list with an other list by linq如何通过linq将列表中的每个值与其他列表进行比较
【发布时间】:2020-10-16 22:50:56
【问题描述】:

我有两个列表:

ListA ListB

Name Name
Marc Marc
micheal micheal
Jolie Jolie

我想比较这两个列表 A 和 B,如果它们具有相同的名称,它应该返回 true。如果一个列表有不同的值,它应该返回 false,如果一个列表有 2 个值,另一个有 3 个值,它也应该返回 false。 即使它们具有相同的值,这种特质也总是使我返回错误:

var SameNames = ListA.All(x => ListB.All(y => y.Name.Equals(x.Name)));

我做错了?请

【问题讨论】:

标签: c# linq


【解决方案1】:
var SameNames = ListA.All(x => ListB.All(y => y.Name.Equals(x.Name)));

您在此处写的是要求 A 中的 所有 名称与 B 中的 所有 名称匹配。这显然与您的示例数据冲突,因为 A 中的“Marc”不等于 B 中的“朱莉”。

只有当 A 和 B 总共只包含一个不同的名称时,您当前的代码才会返回 true。这对您要解决的问题毫无意义。


您实际上要解决的是,如果来自 A 的 所有 名称在 B 中具有 一个 匹配。这可以通过以下方式实现:

var sameNames = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));

注意Any。如果找到(至少)一个匹配项,它将返回true。这与 All 不同,后者仅在 B 中的 所有 值都匹配时返回 true。

但是,这还不够。您现在已经确定 A 中的所有名称都出现在 B 中,但 B 中仍有可能包含更多不在 A 中的名称。以以下示例为例:

var listA = new List<string>() { "Andy", "Bobby", "Cindy" };
var listB = new List<string>() { "Andy", "Bobby", "Cindy", "David" };

var SameNames = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));

SameNames 将是true,但那是因为你只有一个方向。您需要检查两个方向:

var listA_in_listB = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
var listB_in_listA = listB.All(b => listA.Any(a => a.Name.Equals(b.Name)));

var sameNames = listA_in_listB && listB_in_listA;

这会给你想要的结果。


请注意,如何处理此问题还有一些其他变体。

如果您可以保证每个列表不包含任何重复项,那么您可以简单地进行一次检查,然后确认列表长度相等,而不是进行两次相同的检查:

var listA_in_listB = listA.All(a => listB.Any(b => b.Name.Equals(a.Name)));
var sameLength = listA.Length == listB.Length;

var sameNames = listA_in_listB && sameLength;

这更有效,但它确实需要知道您的名字在每个列表中都是唯一的。

【讨论】:

  • 非常感谢!通过您的回答,我可以很好地使用 All 和 Any :) 您的解决方案对我有用,谢谢!
【解决方案2】:

微软有一篇文章in here

您应该创建一个函数来比较每个值。变量不是解决这个问题的方法。

制作一个类似例子的函数:

{
    static void Main()  
    {  
        // Create the IEnumerable data sources.  
        string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt");  
        string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt");  
  
        // Create the query. Note that method syntax must be used here.  
        IEnumerable<string> differenceQuery =  
          names1.Except(names2);  
  
        // Execute the query.  
        Console.WriteLine("The following lines are in names1.txt but not names2.txt");  
        foreach (string s in differenceQuery)  
            Console.WriteLine(s);  
  
        // Keep the console window open in debug mode.  
        Console.WriteLine("Press any key to exit");  
        Console.ReadKey();  
    }  
}  
/* Output:  
     The following lines are in names1.txt but not names2.txt  
    Potra, Cristina  
    Noriega, Fabricio  
    Aw, Kam Foo  
    Toyoshima, Tim  
    Guy, Wey Yuan  
    Garcia, Debra  
     */  

【讨论】:

  • 这也是一个新想法,取所有的不等式,最后只问是否不等,而不是问是否都相等。
  • 请注意,OP 的示例不使用字符串列表。他在比较对象的Name 属性,而这些对象本身并不是字符串。
【解决方案3】:

如果顺序应该是相关的:

bool result = Enumerable.SequenceEquals<string>(ListA, ListB);

如果顺序无关紧要:

bool result = new HashSet<string>(ListA).SetEquals(ListB);

在 LINQ 中过于复杂地执行此操作只是过度调整,此时您可以只使用 ForEachs。如果你想要一个简单而干净的解决方案,这些是给你的。

问候

【讨论】:

  • 请注意,OP 的示例不使用字符串列表。他在比较对象的Name 属性,而这些对象本身并不是字符串。
【解决方案4】:

以下是实现您的要求的代码。

var isEqual = ListA.OrderBy(o=>o.Name).SelectMany(x=>x.Name).SequenceEqual(ListB.OrderBy(o=>o.Name).SelectMany(x => x.Name));

【讨论】:

    【解决方案5】:

    您可以使用var SameNames = ListA.SequenceEquals(ListB)。这将使用对象中定义的默认等号。如果两个对象中的任何一个为空,它将引发异常。所以请确保您的列表不为空。

    等于实现

    class YourListItem {
       public string Name { get; set; }
    
       public override bool Equals(object obj)
       {
            if(obj is YourListItem other) {
                 return this.Name == other.Name;
            }
            return false;
       }
       
       public override int GetHashCode() => this.Name.GetHashCode();
    }
    

    如果您需要一个不真正“适合”对象equals 方法的自定义比较逻辑,或者您需要多个不同的相等实现。您可以为您的类型创建另一个实现IEqualityComparer 的类,并将其作为第二个参数var SameNames = ListA.SequenceEquals(ListB, new MyEqualityComparer()) 传递。

    平等比较器

    class MyEqualityComparer: IEqualityComparer<YourListItem> {
        public bool Equals(YourListItem first, YourListItem second) {
             return first.Name == second.Name;
        }
        public int GetHashCode(YourListItem obj) => obj.Name.GetHashCode();
    }
    

    注意:在覆盖 Equals 时,您也应该始终实现 GetHashCode。如果哈希码不同,它就像一个较短的等号(在某种意义上),等号是多余的,不需要执行。如果 HashCode 不同,许多使用 EqualityComparers 或 Equals 的预构建方法将不会运行 Equals。默认的 HashCode 是 Object 引用,因此它会错误地将两个列表匹配为不相等,而实际上应该相等(如果您不覆盖 HashCode)。为您的 HashCode 实现选择一些您认为对于大多数对象不同的字段。如果你有一个像这样的小对象,它看起来很愚蠢,但是你可以拥有具有复杂 Equals 实现或基于泛型反射的慢速相等检查的巨大对象。因此,当 HashCode 不相同时,大多数时候能够完全跳过平等检查就变得很重要了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-05
      • 2013-06-06
      • 1970-01-01
      • 2019-02-28
      • 1970-01-01
      • 1970-01-01
      • 2019-12-21
      • 1970-01-01
      相关资源
      最近更新 更多