Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多个列表与IEnumerable.Intersect()的交集_C#_.net_Linq - Fatal编程技术网

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();
}