多个列表与IEnumerable.Intersect()交集
我有一个列表,我想find这样的路口:
var list1 = new List<int>() { 1, 2, 3 }; var list2 = new List<int>() { 2, 3, 4 }; var list3 = new List<int>() { 3, 4, 5 }; var listOfLists = new List<List<int>>() { list1, list2, list3 }; // expected intersection is List<int>() { 3 };
有没有办法用IEnumerable.Intersect()做到这一点?
编辑:我应该更清楚这一点:我真的有一个列表的清单,我不知道会有多less,上面的三个列表只是一个例子,我所拥有的实际上是一个IEnumerable<IEnumerable<SomeClass>>
解
感谢所有伟大的答案。 原来有四个选项可以解决这个问题: List + aggregate (@Marcel Gosselin), List + foreach (@JaredPar,@Gabe Moothart), HashSet + aggregate (@jesperll)和HashSet + foreach (@Tony the Pony)。 我对这些解决scheme进行了一些性能testing(不同数量的列表 ,每个列表中元素的 数量和随机数最大大小。
事实certificate,对于大多数情况下,HashSet比List更好(除了大列表和小随机数的大小,由于HashSet的性质我猜)我找不到任何真正的区别foreach方法和聚合方法(foreach方法稍微好一些)。
对我来说,总的方法真的很有吸引力(我正在接受的答案),但我不会说这是最可读的解决scheme..再次感谢所有!
怎么样:
var intersection = listOfLists .Skip(1) .Aggregate( new HashSet<T>(listOfLists.First()), (h, e) => { h.IntersectWith(e); return h; } );
这样,通过使用相同的HashSet并且仍然在一个语句中进行优化。 只要确保listOfLists总是包含至less一个列表。
你确实可以使用Intersect
两次。 不过,我相信这样会更有效率:
HashSet<int> hashSet = new HashSet<int>(list1); hashSet.IntersectWith(list2); hashSet.IntersectWith(list3); List<int> intersection = hashSet.ToList();
当然,这不是一个小问题,但是如果你有很多大集合,这可能是重要的。
基本上Enumerable.Intersect
需要在每个调用中创build一个集合 – 如果你知道你将要做更多的集合操作,你可以保持这个设置。
与往常一样,密切关注性能与可读性 – 两次调用Intersect
的方法链非常有吸引力。
编辑:对于更新的问题:
public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists) { HashSet<T> hashSet = null; foreach (var list in lists) { if (hashSet == null) { hashSet = new HashSet<T>(list); } else { hashSet.IntersectWith(list); } } return hashSet == null ? new List<T>() : hashSet.ToList(); }
或者如果你知道它不会是空的,那么跳过将会相对便宜:
public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists) { HashSet<T> hashSet = new HashSet<T>(lists.First()); foreach (var list in lists.Skip(1)) { hashSet.IntersectWith(list); } return hashSet.ToList(); }
试试这个,它的工作原理,但我真的很想摆脱集合中的.ToList()。
var list1 = new List<int>() { 1, 2, 3 }; var list2 = new List<int>() { 2, 3, 4 }; var list3 = new List<int>() { 3, 4, 5 }; var listOfLists = new List<List<int>>() { list1, list2, list3 }; var intersection = listOfLists.Aggregate((previousList, nextList) => previousList.Intersect(nextList).ToList());
更新:
在@pomber发表评论之后,可以摆脱Aggregate
调用中的ToList()
,并将它移到外面只执行一次。 以前的代码比新代码快,我没有testing性能。 所需的更改是在最后一行中指定Aggregate
方法的genericstypes参数,如下所示:
var intersection = listOfLists.Aggregate<IEnumerable<int>>( (previousList, nextList) => previousList.Intersect(nextList) ).ToList();
你可以做到以下几点
var result = list1.Intersect(list2).Intersect(list3).ToList();
这是我的解决scheme的一个扩展方法,我称为IntersectMany。
public static IEnumerable<TResult> IntersectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) { using (var enumerator = source.GetEnumerator()) { if(!enumerator.MoveNext()) return new TResult[0]; var ret = selector(enumerator.Current); while (enumerator.MoveNext()) { ret = ret.Intersect(selector(enumerator.Current)); } return ret; } }
所以用法是这样的:
var intersection = (new[] { list1, list2, list3 }).IntersectMany(l => l).ToList();
这是我列表ListOfLists(没有相交function)的一行解决scheme:
var intersect = ListOfLists.SelectMany(x=>x).Distinct().Where(w=> ListOfLists.TrueForAll(t=>t.Contains(w))).ToList()
这应该适用于.net 4(或更高版本)
经过searchnetworking,并没有真正提出我喜欢(或工作)的东西,我睡了,并提出了这一点。 我使用一个类( SearchResult
),它有一个EmployeeId
,这是我需要在列表中共同的事情。 我返回每个列表中都有EmployeeId
所有logging。 这不是幻想,但它简单易懂,就是我喜欢的。 对于小列表(我的情况)它应该performance得很好 – 任何人都可以理解它!
private List<SearchResult> GetFinalSearchResults(IEnumerable<IEnumerable<SearchResult>> lists) { Dictionary<int, SearchResult> oldList = new Dictionary<int, SearchResult>(); Dictionary<int, SearchResult> newList = new Dictionary<int, SearchResult>(); oldList = lists.First().ToDictionary(x => x.EmployeeId, x => x); foreach (List<SearchResult> list in lists.Skip(1)) { foreach (SearchResult emp in list) { if (oldList.Keys.Contains(emp.EmployeeId)) { newList.Add(emp.EmployeeId, emp); } } oldList = new Dictionary<int, SearchResult>(newList); newList.Clear(); } return oldList.Values.ToList(); }
下面是一个使用int列表的例子,而不是一个类(这是我的原始实现)。
static List<int> FindCommon(List<List<int>> items) { Dictionary<int, int> oldList = new Dictionary<int, int>(); Dictionary<int, int> newList = new Dictionary<int, int>(); oldList = items[0].ToDictionary(x => x, x => x); foreach (List<int> list in items.Skip(1)) { foreach (int i in list) { if (oldList.Keys.Contains(i)) { newList.Add(i, i); } } oldList = new Dictionary<int, int>(newList); newList.Clear(); } return oldList.Values.ToList(); }
如果您的列表很小,这是一个简单的解决scheme。 如果你有更大的列表,它不像散列表那样执行:
public static IEnumerable<T> IntersectMany<T>(this IEnumerable<IEnumerable<T>> input) { if (!input.Any()) return new List<T>(); return input.Aggregate(Enumerable.Intersect); }