【问题标题】:C#: Compare contents of two IEnumerablesC#:比较两个 IEnumerables 的内容
【发布时间】:2020-07-07 15:39:28
【问题描述】:

是否有一个内置的 linq 方法可以用来确定两个序列是否包含相同的项目,而不考虑顺序?

例如:

{1, 2, 3} == {2, 1, 3}
{1, 2, 3} != {2, 1, 3, 4}
{1, 2, 3} != {1, 2, 4}

你有 SequenceEquals,但是我必须先订购两个序列,不是吗?

【问题讨论】:

标签: c# linq


【解决方案1】:

有很多方法。假设 A 和 B 是 IEnumerable。

!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc

【讨论】:

  • 在那里使用 Intersect,你需要双向进行吗?
  • 我认为这比排序序列要慢得多。除非您正在处理表达式树。
  • query.Count() == 0 与 !query.Any() 相同,但 any 调用更快,因为它不会评估整个列表。
  • 此代码仅在序列是集合时有效,即其中没有重复项
  • 除非我遗漏了一些东西,否则如果枚举中有重复项,这些将不起作用。对于 { 1, 2, 3, 3 } 和 { 1, 2, 2, 3},Except 版本返回 true 如果两者都是 {1,2,3,3},则 Intersect 版本返回 false
【解决方案2】:

如果您不关心重复项(即您认为{1, 2, 3} 等于{1, 2, 3, 2}),那么:

new HashSet<int>(A).SetEquals(B)

(或者任何类型的元素类型,而不是int)。

否则:

public static bool SequenceEqualUnordered<T>(IEnumerable<T> first, IEnumerable<T> second)
{
    if (first == null)
        return second == null; // or throw if that's more appropriate to your use.
    if (second == null)
        return false;   // likewise.
    var dict = new Dictionary<T, int>(); // You could provide a IEqualityComparer<T> here if desired.
    foreach(T element in first)
    {
        int count;
        dict.TryGetValue(element, out count);
        dict[element] = count + 1;
    }
    foreach(T element in second)
    {
        int count;
        if (!dict.TryGetValue(element, out count))
            return false;
        else if (--count == 0)
            dict.Remove(element);
        else
            dict[element] = count;
    }
    return dict.Count == 0;
}

记录第一个序列中的每个元素,然后对照它检查第二个元素。如果第二个序列中有一个太多,则可以返回 false,否则如果计数字典中没有任何内容,则它们是相等的,如果还剩下任何元素,则返回 false。

与使用OrderBy() 后跟O(n) 比较的两种O(n log n) 不同,您有一个O(n) 操作来构建计数集,以及一个O(n) 检查反对它。

【讨论】:

    【解决方案3】:

    有两个 IEnumerables(A 和 B):

    bool equal = (A.Count() == B.Count() && (!A.Except(B).Any() || !B.Except(A).Any()))
    

    我认为这比 except(A).Count 更好,因为不会评估整个 Excep。只要在例外中找到一个元素,它就会停止。使用 Count,评估整个 except。 最重要的是,我们可以通过首先检查 Count 属性来避免评估这些代价高昂的 except。如果计数不相等,那么我们检查例外。

    【讨论】:

    • 爱除了会在第一次差异时产生——很好!
    • Except() 之间的|| 应该是&amp;&amp;,正如answer 所建议的那样。否则,以下情况也将成立:{1,2,3} == {1,2,1}。我不确定它是否正确——至少对我来说不是:)。
    【解决方案4】:

    试试 HashSet 类:

    var enumA = new[] { 1, 2, 3, 4 };
    var enumB = new[] { 4, 3, 1, 2 };
    
    var hashSet = new HashSet<int>(enumA);
    hashSet.SymmetricExceptWith(enumB);
    Console.WriteLine(hashSet.Count == 0); //true => equal
    

    但这只有在值不同的情况下才能正常工作。

    例如

    var enumA = new[] { 1, 1, 1, 2 };
    var enumB = new[] { 1, 2, 2, 2 };
    

    也被认为与上述方法“相等”。

    【讨论】:

    • return hashSet.SetEquals(enumB);
    【解决方案5】:

    按照您的示例,您可以将两个 IEnumerable 都设为 List 类型,然后使用 SequenceEqual 作为下面的示例:

    var first = Enumerable.Range(1, 3);
    var second = Enumerable.Range(1, 3);
    var areTheyEqual = first.ToList().SequenceEqual(second.ToList());
    if (areTheyEqual)
    { /* do something... */}
    

    【讨论】:

    • 此方法无需转换为列表即可工作:)
    【解决方案6】:

    我这样做是为了将新项目合并到一个没有重复的集合中, 它需要两个集合并返回所有项目,没有任何重复

    List<Campaign> nonMatching = (from n in newCampaigns 
    where !(from e in Existing select e.Id).Contains<int>(n.Id) 
    select n).ToList<Campaign>();
    

    现在通过删除 !对于包含语句

    List<Campaign> nonMatching = (from n in newCampaigns 
    where (from e in Existing select e.Id).Contains<int>(n.Id) 
    select n).ToList<Campaign>();
    

    它会返回重复的

    【讨论】:

      【解决方案7】:

      为了比较两个对象中的数据,我简单地用了这个

      A.Except(B).Any() || B.Except(A).Any()
      

      【讨论】:

      • 如果我的布尔代数是正确的,那就等于!A.Except(B).Any() &amp;&amp; !B.Except(A).Any(),但看起来更干净一些。 ?
      • 正确@Svish。我相信这完全取决于开发人员,您想将其用于什么目的。
      • @Svish 澄清一下,!A.Except(B).Any() &amp;&amp; !B.Except(A).Any() 不等于 A.Except(B).Any() || B.Except(A).Any()。这是逆逻辑。前者检查逐项等效性;后者检查逐项不等价。
      【解决方案8】:

      我认为对序列进行排序是实现这一目标的最快方式。

      【讨论】:

      • 对于 SequenceEquals 它必须是相同的顺序。
      • "SequenceEqual)>)(IEnumerable)>), IEnumerable)>)) 方法枚举了这两个源序列并行,并使用 TSource 的默认相等比较器,默认比较相应的元素。"
      • 是的,我误读了这个问题。这是 O(nlogn),这是算法为此目的可以达到的最快渐近时间。
      • 理论上,最快的算法是对一个排序,然后在排序后的版本中对另一个的每个元素进行二分查找。这确实需要手动编写代码。另一种快速算法是建立其中一个的字典并通过它查找另一个的元素。
      • Except是一个O(n) op,其中排序至少是O(n log n)
      【解决方案9】:

      如果你真的只是在测试是否有重复,那么 leppie 的建议应该有效:

      if (A.Except(B).Count == 0 && B.Except(A).Count == 0) {...}
      

      但如果你只需要得到一个没有重复的 IEnumerable:

      var result = A.Union(B).Distinct();
      

      【讨论】:

        猜你喜欢
        • 2011-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多