Check if two lists are equal
我的课程如下:
1 2 3 4
| public class Tag {
public Int32 Id { get; set; }
public String Name { get; set; }
} |
我有两个标签列表:
1 2
| List<Tag> tags1;
List<Tag> tags2; |
我使用Linq的Select获取每个标签列表的ID。然后:
1 2 3 4 5
| 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 }; |
IDS1应等于IDS2和IDS3…两个都有相同的数字。
IDS1不应等于IDS4和IDS5…
我尝试了以下方法:
1 2
| var a = ints1.Equals(ints2);
var b = ints1.Equals(ints3); |
但两者都给了我虚假的。
检查标签列表是否相等的最快方法是什么?
更新
我在找标签和书上的标签完全一样的帖子。
1 2 3 4 5 6 7 8 9 10
| 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(); |
我使用的是实体框架,我得到了错误:
An exception of type 'System.NotSupportedException' occurred in mscorlib.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'Boolean SetEquals(System.Collections.Generic.IEnumerable`1[System.Int32])' method, and this method cannot be translated into a store expression.
我如何解决这个问题?
- 你所说的不平等是什么意思,你是说所有的元素都应该是不同的,或者只是它们不应该包含相同的元素,至少应该有一个不同的元素?
- 您的序列ids5包含重复项。这是故意的吗?
- @Selman22我的意思是这两个列表应该包含完全相同的元素…命令无关紧要
- @dasbinkenlight是的,因为在这种情况下,ID是唯一的,因为它们是主键。
- 您可能希望单独发布更新后的问题,因为编辑后问题将有一个完全不同于目前发布的任何问题的解决方案。添加[EF]标记,并确保新问题的标题显示"比较ef的where子句中的列表"或类似的内容。
- 这里的答案与标记的副本有很大的不同,因为它专门处理值类型列表,而不是另一个问题,即引用类型列表。由于要处理引用类型,链接问题的答案要复杂得多。提名重新开始
使用SequenceEqual检查序列是否相等,因为Equals方法检查引用是否相等。
1
| var a = ints1.SequenceEqual(ints2); |
或者,如果您不关心元素顺序,请使用Enumerable.All方法:
1
| var a = ints1.All(ints2.Contains); |
第二个版本还需要对Count进行另一个检查,因为即使ints2包含的元素多于ints1,它也会返回true。所以更正确的版本应该是这样的:
1
| var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count; |
为了检验不等式,只需颠倒All方法的结果:
1
| var a = !ints1.All(ints2.Contains) |
- 也许使用except和interect会是更好的选择?我不确定您的解决方案在集成到Linq to Entities查询时是否快速,因为我需要多次选择所有ID…或者我错了?
- 我不确定。您需要同时尝试这两种方法并查看生成的SQL。顺便问一下,您是否尝试过此解决方案?你有没有得到NotSupportedException或者转换成功?
- 速度慢,不处理重复项。8
- @根据Op在问题中的评论,重复的代码并不重要。
- 注意:您可能习惯于将.All与lambda(如.All(i=> ints2.Contains(i)))一起使用,但由于list.contains()与获取int并返回bool的函数签名匹配,因此他直接将函数名作为谓词传递。基本上与ints1.All(i=> ints2.Contains(i))相同。只是想指出这一点,以防像我这样的人最初感到困惑。
- 几乎犯了一个错误,因为我实现了您的第二个示例,没有进一步阅读,需要查看计数检查(第三个示例)。:)
- 我会把var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count;的顺序改为var a = ints1.Count == ints2.Count && ints1.All(ints2.Contains);的顺序。简单的计数比较可能比.All调用快得多。如果计数不相等,则返回速度更快。
- 我将否决这个答案,因为这不适用于所有情况,列表A A、A和列表B包含B、A现在的列表B。所有(列表A.contains)和列表A.all(列表B.contains)将给出不同的结果,因为两个列表都有相同的计数,我们将在其中一个列表中得到正确的结果,即使两个列表都不同,如果一个列表有多个条目-
- 希望当ints2为{1,2,3}时,ints1不是{1,1,2}。因为这个答案会说它们是等价的。
- 为什么我得到一个关于ints2.contains的错误?
List相等不逐元素检查它们。您可以使用Linq的SequenceEqual方法:
1
| var a = ints1.SequenceEqual(ints2); |
若要忽略订单,请使用SetEquals:
1
| var a = new HashSet <int>(ints1 ).SetEquals(ints2 ); |
这应该有效,因为您正在比较不包含重复项的ID序列。如果是这样,并且您需要考虑重复,那么在线性时间内这样做的方法是编写一个基于哈希的计数字典,为第一个序列的每个元素添加一个,为第二个序列的每个元素减去一个,并检查结果计数是否都为零:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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); |
最后,如果您对O(N*LogN)解决方案不满意,可以对这两个序列进行排序,并使用SequenceEqual比较它们是否相等。
- 对于SequenceEqual,元素的顺序必须相同-op希望以任何顺序使用相同的元素。
- @你说得对,我一开始就错过了。现在应该没事了。
- 在编辑之前,您的回答是错误的,因为OP希望忽略订单,现在SetEquals不考虑重复项(OP不清楚)。
- @Timschmeller这就是编辑的目的。编辑后情况如何?
- @如果两个序列中的任何一个含有双链酯,则dasbinkenlight仍然不起作用-{1, 1, 2, 3}将"等于"{1, 2, 2, 3}。
- @dTanley一个Ids序列不可能包含重复项。
- @达斯布林肯利:也许像这样的事情会奏效:bool isEqual = ids1.Count == ids2.Count; if (isEqual) isEqual = ids1.OrderBy(i=>i).SequenceEqual(ids2.OrderBy(i => i));。
- @当然可以。事实上,这首歌中的第五个片段是这样的。
- @我刚刚问了他一个问题。
- @我刚刚更新了我的示例…我也在寻找一个解决方案,可以在linq-to-entities查询中使用…
- @MDMOURA我明白了-所以不仅需要比较这两个列表,而且还需要在数据库中比较它们,因为条件是ef中Where子句的一部分?这是一个更有趣的问题,我得想一想怎么做。
- 是的,基本上一本书包含一组标签…所以给一本书,我需要得到所有贴有和那本书完全相同标签的帖子。当然,秩序不重要……
1 2
| Enumerable.SequenceEqual(FirstList.OrderBy(fElement => fElement),
SecondList.OrderBy(sElement => sElement)) |
- lambda参数的名称很奇怪。它们不是列表,它们是元素。我要么在操作环境中使用Id,要么在通用环境中使用element。
- 这家伙显然是从其他地方抄袭了这个答案,因为从参数名称来看,他甚至不知道它做了什么。
- 我认为它正好涵盖了手头的问题。它比较两个列表,不管顺序如何。