C# 多个列表与IEnumerable.Intersect()的交集
我有一个列表,我想找到这样的交叉点:C# 多个列表与IEnumerable.Intersect()的交集,c#,.net,linq,C#,.net,Linq,我有一个列表,我想找到这样的交叉点: 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
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 };
var intersection = (new[] { list1, list2, list3 }).IntersectMany(l => l).ToList();
var list1=newlist(){1,2,3};
var list2=新列表(){2,3,4};
var list3=新列表(){3,4,5};
var listOfLists=new List(){list1,list2,list3};
//期望的交集是List(){3};
有没有办法用IEnumerable.Intersect()实现这一点
编辑:
我应该更清楚一点:我真的有一个列表,我不知道会有多少,上面的三个列表只是一个例子,我实际上有一个IEnumerable
解决方案
谢谢你的回答。解决这个问题的方法有四种:List+aggregate(@Marcel Gosselin)、List+foreach(@JaredPar,@Gabe Moothart)、HashSet+aggregate(@jesperll)和HashSet+foreach(@Tony the Pony)。我对这些解决方案进行了一些性能测试(改变列表的数量,每个列表中的元素数量,以及随机数最大值大小)
事实证明,在大多数情况下,哈希集的性能都比列表好(除了大列表和小随机数大小之外,我想这是因为哈希集的性质)
我找不到foreach方法和aggregate方法之间的任何真正区别(foreach方法的性能稍好一些)
对我来说,聚合方法确实很有吸引力(我将以此作为公认的答案),但我不会说它是最具可读性的解决方案。再次感谢大家!您可以做以下几点
var result = list1.Intersect(list2).Intersect(list3).ToList();
您确实可以使用
Intersect
两次。但是,我相信这样会更有效:
HashSet<int> hashSet = new HashSet<int>(list1);
hashSet.IntersectWith(list2);
hashSet.IntersectWith(list3);
List<int> intersection = 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();
}
public List IntersectAll(IEnumerable List)
{
HashSet HashSet=新的HashSet(lists.First());
foreach(列表中的变量列表。跳过(1))
{
hashSet.IntersectWith(列表);
}
返回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());
那么:
var intersection = listOfLists
.Skip(1)
.Aggregate(
new HashSet<T>(listOfLists.First()),
(h, e) => { h.IntersectWith(e); return h; }
);
var交叉点=列表
.Skip(1)
.合计(
新哈希集(listOfLists.First()),
(h,e)=>{h.与(e)相交;返回h;}
);
通过这种方式,它通过在整个语句中使用相同的哈希集进行优化,并且仍然在单个语句中使用。只需确保ListofList始终包含至少一个列表。这是我使用扩展方法的解决方案版本,我称之为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;
}
}
这是我的列表列表(ListOfLists)的单行解决方案,没有intersect函数:
var intersect = ListOfLists.SelectMany(x=>x).Distinct().Where(w=> ListOfLists.TrueForAll(t=>t.Contains(w))).ToList()
这应该适用于.net 4(或更高版本)如果列表都很小,这是一个简单的解决方案。如果列表较大,则其性能不如哈希集:
public static IEnumerable<T> IntersectMany<T>(this IEnumerable<IEnumerable<T>> input)
{
if (!input.Any())
return new List<T>();
return input.Aggregate(Enumerable.Intersect);
}
公共静态IEnumerable IntersectMany(此IEnumerable输入)
{
如果(!input.Any())
返回新列表();
返回输入.聚合(可枚举.相交);
}
在搜索“网络”后,我没有真正找到我喜欢的(或有效的)东西,我在上面睡了一觉,然后找到了这个。我的使用了一个类(搜索结果
)这里面有一个EmployeeId
,这是我需要在列表中常见的东西。我返回每个列表中都有EmployeeId
的所有记录。这并不奇怪,但它简单易懂,就像我喜欢的那样。对于小列表(我的案例),它应该表现得很好,任何人都能理解它
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();
}
私有列表GetFinalSearchResults(IEnumerable列表)
{
字典oldList=新字典();
Dictionary newList=新字典();
oldList=lists.First().ToDictionary(x=>x.EmployeeId,x=>x);
foreach(列表中的列表。跳过(1))
{
foreach(列表中的搜索结果emp)
{
if(oldList.key.Contains(emp.EmployeeId))
{
newList.Add(emp.EmployeeId,emp);
}
}
oldList=新字典(newList);
newList.Clear();
}
返回oldList.Values.ToList();
}
下面是一个仅使用int列表而不是类的示例(这是我最初的实现)
静态列表FindCommon(列表项)
{
字典oldList=新字典();
Dictionary newList=新字典();
oldList=项[0]。ToDictionary(x=>x,x=>x);
foreach(在项目中列出。跳过(1))
{
foreach(列表中的int i)
{
if(oldList.Keys.Contains(i))
{
添加(i,i);
}
}
oldList=新字典(newList);
newList.Clear();
}
返回oldList.Values.ToList();
}
谢谢,但我确实有一个列表列表,而不是三个单独的列表。我需要一个独立于列表中的列表数量的列表。@Oskar你可以很容易地循环运行它。谢谢,我只是尝试了一下,它就行了!没有使用聚合()以前,但我想我一直在寻找这样的答案。正如我在对Tony答案的评论中所指出的,我相信他的解决方案会表现得更好。您可以去掉.ToList()如果您使用Aggregate@pomber,我不敢相信你的评论已经三年没有投票了。今天是你的日子,我的朋友。是的,foreach是有意义的。与Marcel答案中的聚合方法相比,这有什么性能差异吗?@Oskar:是的,我的答案使用单个哈希集,而不是每次创建一个新的哈希集。但是,您仍然可以将聚合与集合一起使用…will edit.Ick…只是尝试计算出一个聚合解决方案,这很棘手,因为HashSet.IntersectWith返回null:(嗨,关于IntersectAll()
方法的一个问题(很难):有没有简单的方法添加选择器作为参数来比较值(例如:Func选择器
)并且仍然使用InsertectWith()
?@t
public static IEnumerable<T> IntersectMany<T>(this IEnumerable<IEnumerable<T>> input)
{
if (!input.Any())
return new List<T>();
return input.Aggregate(Enumerable.Intersect);
}
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();
}
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();
}