使用LINQ,我有一个列表的列表,如何选择存在于每个列表中的所有对象?

3

我有一个列表的列表:

List<Tuple<string, List<SomeObject>>

我想选择所有在上述列表的每一行中都存在的SomeObjects

有些对象只存在于一个或两个列表中,但我想要存在于每个列表中的所有对象,而且其他对象被舍弃。

我无法找到一种优雅的解决方案,而不需要大量使用C#代码。有没有更好的方法?


1
你尝试过 LINQ 的 .SelectMany 吗? - danludwig
@danludwig所说的,比我快了一秒钟。 - user604613
4个回答

7
list.Select (x => x.Item2 as IEnumerable<SomeObject>)
    .Aggregate((x,y)=> x.Intersect(y))
    .ToList();

或者像Jeppe Stig Nielsen建议的那样(我认为这更加优雅):

list.Select(x => x.Item2.AsEnumerable())
    .Aggregate(Enumerable.Intersect)
    .ToList();

不需要 as IEnumerable<SomeObject> - dcastro
@TimSchmelter 为什么? - dcastro
1
我认为这是因为实际上没有一个List<T>.Intersect。它是在Enumerable类中定义的,并且返回一个IEnumerable<T>,而不是List<T>。而且,这个Aggregate重载需要两个参数和返回值都是相同类型。不知何故,返回值为IEnumerable<T>并不足以让类型推断知道两个参数也必须是IEnumerable<T>。 - Juan Lopes
1
当然,你的解决方案也可以写成 list.Select(x => x.Item2.AsEnumerable()).Aggregate(Enumerable.Intersect).ToList(); - Jeppe Stig Nielsen
2
Aggregate<> 相关的重载只有一个类型参数 TSource。您需要让 TSource 成为 IEnumerable<SomeObject>,而不是 List<SomeObject>。因此,包含 "as enumerable" 可以帮助解决这个问题。当然,另一种选择是显式地给出类型参数。强调一下,也可以使用 TSource == List<SomeObject>list.Select(x => x.Item2).Aggregate((x, y) => x.Intersect(y).ToList());(不建议使用,每次迭代都会创建新的 List<> 实例)。 - Jeppe Stig Nielsen
显示剩余3条评论

1
我理解您需要多个列表的交集:
var results = source.First().Item2
foreach (var element in source.Skip(1) )
{
    results = results.Intersect(element.Item2)
}

1
强烈受到Juan Lopes精美答案的启发,您可以定义此扩展:
static IEnumerable<TSource> IntersectMany<TSource>(this IEnumerable<IEnumerable<TSource>> sources)
{
  return sources.Aggregate(Enumerable.Intersect);
}

那么这个就可以工作了:

var result = list.Select(x => x.Item2).IntersectMany();

它之所以有效,是因为IEnumerable<out T>T方面是协变的(C# 4,.NET 4.0)。


-1

假设该类重写了EqualsGetHashCode方法或您拥有自定义的IEqualityComparer<SomeObject>,则可以使用以下查询,它使用Enumerable.All

var result = list
    .SelectMany(t => t.Item2)   // select all objects
    .Distinct()                 // just an optimization since duplicates are guaranteed
    .Where(obj => list.All(t => t.Item2.Contains(obj))); 

这是我的样本数据:

var list = new List<Tuple<string, List<SomeObject>>>();
list.Add(Tuple.Create("a", new List<SomeObject> { new SomeObject { ID = 1 }, new SomeObject { ID = 2 }, new SomeObject { ID = 4 } }));
list.Add(Tuple.Create("b", new List<SomeObject> { new SomeObject { ID = 1 }, new SomeObject { ID = 2 }, new SomeObject { ID = 3 } }));
list.Add(Tuple.Create("c", new List<SomeObject> { new SomeObject { ID = 1 }, new SomeObject { ID = 2 }, new SomeObject { ID = 3 } }));
list.Add(Tuple.Create("d", new List<SomeObject> { new SomeObject { ID = 1 }, new SomeObject { ID = 2 }, new SomeObject { ID = 3 } }));

只有ID为1或2的SomeObjects在所有列表中,这是查询结果。


有什么原因导致你给了负评还是只是无聊?这不是最高效或最短的方法,但这是对需求的直接翻译,不是吗? - Tim Schmelter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接