检查一个IEnumerable是否包含另一个IEnumerable的所有元素
比较两个集合中每个元素的字段/属性时,确定一个IEnumerable是否包含另一个IEnumerable的所有元素的最快方法是什么?
public class Item { public string Value; public Item(string value) { Value = value; } } //example usage Item[] List1 = {new Item("1"),new Item("a")}; Item[] List2 = {new Item("a"),new Item("b"),new Item("c"),new Item("1")}; bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2) { var list1Values = list1.Select(item => item.Value); var list2Values = list2.Select(item => item.Value); return //are ALL of list1Values in list2Values? } Contains(List1,List2) // should return true Contains(List2,List1) // should return false
除非您跟踪并维护某个确定一个集合中的所有值是否包含在另一个集合中的状态,否则没有“快速方法”来执行此操作。 如果你只有IEnumerable<T>
来处理,我会使用Intersect
。
var allOfList1IsInList2 = list1.Intersect(list2).Count() == list1.Count();
这样做的性能应该是非常合理的,因为Intersect()
只会枚举每个列表一次。 此外,如果基础types是ICollection<T>
而不是仅仅IEnumerable<T>
,则对Count()
的第二次调用将是最佳的。
您也可以使用Except从第一个列表中删除第二个列表中存在的所有值,然后检查是否所有值都已被删除:
var allOfList1IsInList2 = !list1.Except(list2).Any();
这种方法的优点是不需要两次调用Count()。
C#3.5+
使用Enumerable.All<TSource>
来确定List1中是否包含所有List2项目:
bool hasAll = list2Uris.All(itm2 => list1Uris.Contains(itm2));
当list1包含甚至超过list2的所有项目时,这也将工作。
标记为答案的解决scheme在重复的情况下将失败。 如果您的IEnumerable只包含不同的值,那么它会通过。
下面的答案是2个重复列表:
int aCount = a.Distinct().Count(); int bCount = b.Distinct().Count(); return aCount == bCount && a.Intersect(b).Count() == aCount;
肯特的答案很好,但总的来说,他提供的解决scheme总是需要对整个第一个集合进行迭代。 这里是源代码:
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) { if (first == null) throw Error.ArgumentNull("first"); if (second == null) throw Error.ArgumentNull("second"); return Enumerable.IntersectIterator<TSource>(first, second, comparer); } private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) { Set<TSource> set = new Set<TSource>(comparer); foreach (TSource source in second) set.Add(source); foreach (TSource source in first) { if (set.Remove(source)) yield return source; } }
这并不总是要求的。 所以,这是我的解决scheme:
public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> subset, IEqualityComparer<T> comparer) { var hashSet = new HashSet<T>(subset, comparer); if (hashSet.Count == 0) { return true; } foreach (var item in source) { hashSet.Remove(item); if (hashSet.Count == 0) { break; } } return hashSet.Count == 0; }
其实,你应该考虑使用ISet<T>
( HashSet<T>
)。 它包含所有必需的设置方法。 IsSubsetOf
在你的情况。
Linq运算符SequenceEqual也可以工作(但对枚举的项目是相同的顺序敏感)
return list1Uris.SequenceEqual(list2Uris);
你可以用这个方法比较两个列表
//Method to compare two list private bool Contains(IEnumerable<Item> list1, IEnumerable<Item> list2) { bool result; //Get the value var list1WithValue = list1.Select(s => s.Value).ToList(); var list2WithValue = list2.Select(s => s.Value).ToList(); result = !list1WithValue.Except(list2WithValue).Any(); return result; }