检查两个列表是否相等

Mig*_*ura 156 c# linq

我有一个课程如下:

public class Tag {
    public Int32 Id { get; set; }
    public String Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我有两个标签列表:

List<Tag> tags1;
List<Tag> tags2;
Run Code Online (Sandbox Code Playgroud)

我使用LINQ的select来获取每个标签列表的ID.然后:

List<Int32> ids1 = new List<Int32> { 1, 2, 3, 4 };
List<Int32> ids2 = new List<Int32> { 1, 2, 3, 4 };
List<Int32> ids3 = new List<Int32> { 2, 1, 3, 4 };
List<Int32> ids4 = new List<Int32> { 1, 2, 3, 5 };
List<Int32> ids5 = new List<Int32> { 1, 1, 3, 4 };
Run Code Online (Sandbox Code Playgroud)

ids1应该等于ids2和ids3 ......两者都有相同的数字.

ids1不应该等于ids4和ids5 ......

我尝试了以下方法:

var a = ints1.Equals(ints2);
var b = ints1.Equals(ints3);
Run Code Online (Sandbox Code Playgroud)

但两者都给我错误.

检查标签列表是否相等的最快方法是什么?

UPDATE

我正在寻找TAGS与BOOK中的TAGS完全相同的POSTS.

IRepository repository = new Repository(new Context());

IList<Tags> tags = new List<Tag> { new Tag { Id = 1 }, new Tag { Id = 2 } };

Book book = new Book { Tags = new List<Tag> { new Tag { Id = 1 }, new Tag { Id = 2 } } };

var posts = repository
  .Include<Post>(x => x.Tags)
  .Where(x => new HashSet<Int32>(tags.Select(y => y.Id)).SetEquals(book.Tags.Select(y => y.Id)))
  .ToList();
Run Code Online (Sandbox Code Playgroud)

我正在使用实体框架,我得到错误:

mscorlib.dll中出现"System.NotSupportedException"类型的异常,但未在用户代码中处理

附加信息:LINQ to Entities无法识别方法'Boolean SetEquals(System.Collections.Generic.IEnumerable`1 [System.Int32])'方法,并且此方法无法转换为商店表达式.

我该如何解决这个问题?

Sel*_*enç 289

使用SequenceEqual检查,因为序列平等Equals的方法检查引用相等.

var a = ints1.SequenceEqual(ints2);
Run Code Online (Sandbox Code Playgroud)

或者如果你不关心元素顺序使用Enumerable.All方法:

var a = ints1.All(ints2.Contains);
Run Code Online (Sandbox Code Playgroud)

第二个版本还需要另一个检查,Count因为即使ints2包含的元素多于它,它也会返回true ints1.所以更正确的版本是这样的:

var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count;
Run Code Online (Sandbox Code Playgroud)

为了检查不等式,只需反转All方法的结果:

var a = !ints1.All(ints2.Contains)
Run Code Online (Sandbox Code Playgroud)

  • 我将恢复`var a = ints1.All(ints2.Contains)&& ints1.Count == ints2.Count;`到`var a = ints1.Count == ints2.Count && ints1.All(ints2.)的顺序.含);`.简单的计数比较可能比`.All`调用快得多.如果计数不相等,它会更快地返回. (21认同)
  • 注意:您可能习惯使用`.All`和lambda,如`.All(i => ints2.Contains(i))`,但是因为list.Contains()匹配取'int`的函数签名返回一个`bool`,然后他直接传递函数名作为谓词.基本上与`ints1.All(i => ints2.Contains(i))`相同.只是想指出这一点,以防其他像我这样的人最初感到困惑. (12认同)
  • 慢,不处理重复.`[1,1,2]!= [1,2,2]` (6认同)
  • 我将低估这个答案,因为这不适用于所有情况,列表A {a,a}和列表B包含{b,a}现在ListB.All(listA.contains)和LIstA.All(listB.contains)将给出不同的结果,因为两者都有相同的计数,我们将在其中一个中得到真实,即使两者都不同,如果列表有多个条目,它将无法工作 - (5认同)
  • @CodesInChaos 根据OP在问题重复中的评论并不重要 (2认同)

das*_*ght 109

List<T>等式不会逐个元素地检查它们.您可以使用LINQ的SequenceEqual方法:

var a = ints1.SequenceEqual(ints2);
Run Code Online (Sandbox Code Playgroud)

要忽略订单,请使用SetEquals:

var a = new HashSet<int>(ints1).SetEquals(ints2);
Run Code Online (Sandbox Code Playgroud)

这应该有效,因为您正在比较不包含重复项的ID序列.如果确实如此,并且你需要考虑重复,那么在线性时间内完成它的方法是组成一个基于哈希的计数字典,为第一个序列的每个元素添加一个,为第二个序列的每个元素减去一个序列,并检查结果计数是否全为零:

var counts = ints1
    .GroupBy(v => v)
    .ToDictionary(g => g.Key, g => g.Count());
var ok = true;
foreach (var n in ints2) {
    int c;
    if (counts.TryGetValue(n, out c)) {
        counts[n] = c-1;
    } else {
        ok = false;
        break;
    }
}
var res = ok && counts.Values.All(c => c == 0);
Run Code Online (Sandbox Code Playgroud)

最后,如果您对O(N*LogN)解决方案没问题,可以对两个序列进行排序,并使用它们进行相等比较SequenceEqual.

  • @TimSchmelter这就是编辑的目的.在编辑之后它看起来如何? (3认同)
  • @dasblinkenlight 当然可以。事实上,OP 的 exmaple 中的第五个序列_确实_。 (2认同)

Pan*_*kaj 24

Enumerable.SequenceEqual(FirstList.OrderBy(fElement => fElement), 
                         SecondList.OrderBy(sElement => sElement))
Run Code Online (Sandbox Code Playgroud)

  • lambda参数的名称很奇怪.他们不是名单,他们是一个元素.我要么在OP的上下文中使用`id`,要么在泛型上下文中使用`element`. (8认同)
  • 我认为它恰好涵盖了当前的问题。它比较两个列表,无论顺序如何。 (2认同)