我有两个不同类型的对象集合。让我们称他们为 ALPHA ,然后输入 BRAVO 。这些类型中的每一种都具有属性,该属性是对象的“ID”。在课程中没有ID重复,因此对于任何给定的ID,最多只有一个 ALPHA 和一个 BRAVO 实例。我需要做的是将它们分为3类:
在所有3个案例中,我需要从手中的集合中获取实际对象以供后续操作。
我知道#3的情况,我可以这样做:
var myCorrelatedItems = myAlphaItems.Join(myBravoItems, alpha => alpha.Id, beta => beta.Id, (inner, outer) => new
{
alpha = inner,
beta = outer
});
我也可以编写类似
的#1和#2案例的代码var myUnmatchedAlphas = myAlphaItems.Where(alpha=>!myBravoItems.Any(bravo=>alpha.Id==bravo.Id));
同样适用于unMatchedBravos。不幸的是,这会导致多次迭代alphas(可能非常大!)的集合,以及bravos(也可能非常大!)的集合多次。
有没有办法统一这些查询概念,以尽量减少列表的迭代?这些集合可以有数千个项目。
答案 0 :(得分:2)
如果您只对ID感兴趣,
var alphaIds = myAlphaItems.Select(alpha => alpha.ID);
var bravoIds = myBravoItems.Select(bravo => bravo.ID);
var alphaIdsNotInBravo = alphaIds.Except(bravoIds);
var bravoIdsNotInAlpha = bravoIds.Except(alphaIds);
如果你想要alphas和bravos本身,
var alphaIdsSet = new HashSet<int>(alphaIds);
var bravoIdsSet = new HashSet<int>(bravoIds);
var alphasNotInBravo = myAlphaItems
.Where(alpha => !bravoIdsSet.Contains(alpha.ID));
var bravosNotInAlpha = myBravoItems
.Where(bravo => !alphaIdsSet.Contains(bravo.ID));
编辑: 其他一些选择:
ExceptBy
method的MoreLinq。Enumerable.ToDictionary
方法。IHasId
接口),则可以编写自己的IEqualityComparer<T>
实现; Enumerable.Except
has an overload接受相等比较器作为参数。答案 1 :(得分:1)
有时LINQ不是答案。这是我考虑将HashSet<T>
与自定义比较器一起使用来减少执行集合操作的工作的问题。 HashSets在执行集合操作方面比列表更有效 - 而且(取决于数据)可以大大减少工作量:
// create a wrapper class that can accomodate either an Alpha or a Bravo
class ABItem {
public Object Instance { get; private set; }
public int Id { get; private set; }
public ABItem( Alpha a ) { Instance = a; Id = a.Id; }
public ABItem( Bravo b ) { Instance = b; Id = b.Id; }
}
// comparer that compares Alphas and Bravos by id
class ABItemComparer : IComparer {
public int Compare( object a, object b ) {
return GetId(a).Compare(GetId(b));
}
private int GetId( object x ) {
if( x is Alpha ) return ((Alpha)x).Id;
if( x is Bravo ) return ((Bravo)x).Id;
throw new InvalidArgumentException();
}
}
// create a comparer based on comparing the ID's of ABItems
var comparer = new ABComparer();
var hashAlphas =
new HashSet<ABItem>(myAlphaItems.Select(x => new ABItem(x)),comparer);
var hashBravos =
new HashSet<ABItem>(myBravoItems.Select(x => new ABItem(x)),comparer);
// items with common IDs in Alpha and Bravo sets:
var hashCommon = new HashSet<Alpha>(hashAlphas).IntersectWith( hashSetBravo );
hashSetAlpha.ExceptWith( hashSetCommon ); // items only in Alpha
hashSetBravo.ExceptWith( hashSetCommon ); // items only in Bravo
答案 2 :(得分:1)
这是一个可能的LINQ解决方案,它在两个集合上执行完全外部联接,并向它们附加一个属性,显示它们属于哪个组。但是,当您尝试将组分成不同的变量时,此解决方案可能会失去光彩。这一切都取决于您需要对这些对象执行何种操作。无论如何,这个(我认为)在5000件物品的清单上以可接受的速度(.5秒)运行:
var q =
from g in
(from id in myAlphaItems.Select(a => a.ID).Union(myBravoItems.Select(b => b.ID))
join a in myAlphaItems on id equals a.ID into ja
from a in ja.DefaultIfEmpty()
join b in myBravoItems on id equals b.ID into jb
from b in jb.DefaultIfEmpty()
select (a == null ?
new { ID = b.ID, Group = "Bravo Only" } :
(b == null ?
new { ID = a.ID, Group = "Alpha Only" } :
new { ID = a.ID, Group = "Both" }
)
)
)
group g.ID by g.Group;
您可以删除“分组依据”查询或从此(q.ToDictionary(x => x.Key, x => x.Select(y => y))
)或其他任何内容创建字典!这只是一种对商品进行分类的方法。我确信那里有更好的解决方案,但这似乎是一个非常有趣的问题,所以我想我也可以试一试!
答案 3 :(得分:1)
Dictionary<int, Alpha> alphaDictionary = myAlphaItems.ToDictionary(a => a.Id);
Dictionary<int, Bravo> bravoDictionary = myBravoItems.ToDictionary(b => b.Id);
ILookup<string, int> keyLookup = alphaDictionary.Keys
.Union(bravoDictionary.Keys)
.ToLookup(x => alphaDictionary.ContainsKey(x) ?
(bravoDictionary.ContainsKey(x) ? "both" : "alpha") :
"bravo");
List<Alpha> alphaBoth = keyLookup["both"].Select(x => alphaDictionary[x]).ToList();
List<Bravo> bravoBoth = keyLookup["both"].Select(x => bravoDictionary[x]).ToList();
List<Alpha> alphaOnly = keyLookup["alpha"].Select(x => alphaDictionary[x]).ToList();
List<Bravo> bravoOnly = keyLookup["bravo"].Select(x => bravoDictionary[x]).ToList();
答案 4 :(得分:0)
如果您想遍历并比较最少的次数,我认为LINQ不是解决此问题的最佳方法。我认为以下迭代解决方案更具性能。我相信代码可读性不受影响。
var dictUnmatchedAlphas = myAlphaItems.ToDictionary(a => a.Id);
var myCorrelatedItems = new List<AlphaAndBravo>();
var myUnmatchedBravos = new List<Bravo>();
foreach (Bravo b in myBravoItems)
{
var id = b.Id;
if (dictUnmatchedAlphas.ContainsKey(id))
{
var a = dictUnmatchedAlphas[id];
dictUnmatchedAlphas.Remove(id); //to get just the unmatched alphas
myCorrelatedItems.Add(new AlphaAndBravo { a = a, b = b});
}
else
{
myUnmatchedBravos.Add(b);
}
}
AlphaAndBravo的定义:
public class AlphaAndBravo {
public Alpha a { get; set; }
public Bravo b { get; set; }
}