C# LINQ组合查询
我有两个不同类型的对象集合。让我们称他们为typeALPHA和typeBRAVO。每种类型都有一个属性,即对象的“ID”。类中没有重复的ID,因此对于任何给定ID,最多有一个ALPHA和一个BRAVO实例。我需要做的是将它们分为三类:C# LINQ组合查询,c#,.net,linq,query-optimization,C#,.net,Linq,Query Optimization,我有两个不同类型的对象集合。让我们称他们为typeALPHA和typeBRAVO。每种类型都有一个属性,即对象的“ID”。类中没有重复的ID,因此对于任何给定ID,最多有一个ALPHA和一个BRAVO实例。我需要做的是将它们分为三类: ALPHA中未出现在BRAVO集合中的ID实例 BRAVO中未出现在ALPHA集合中的ID实例 出现在两个集合中的ID实例 在所有3种情况下,我都需要手头有集合中的实际对象,以便进行后续操作 我知道对于#3的情况,我可以做如下事情: var myCorrelat
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));
对于不匹配的RAVOS也是如此。不幸的是,这将导致alphas集合(可能非常大!)多次迭代,bravos集合(也可能非常大!)也会多次迭代
有没有办法统一这些查询概念,从而最小化列表上的迭代?这些集合可以有数千项。如果您只对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);
如果你想要Alpha和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));
var alphaIdsSet=新哈希集(alphaIds);
var bravoIdsSet=新哈希集(bravoIds);
var alphasNotInBravo=我的AlphaItems
其中(alpha=>!bravoIdsSet.Contains(alpha.ID));
var bravosNotInAlpha=myBravoItems
.Where(bravo=>!alphaidset.Contains(bravo.ID));
编辑:
其他一些选择:
Enumerable.ToDictionary
方法IHasId
接口),则可以编写自己的IEqualityComparer
实现<代码>可枚举。接受相等比较器作为参数的除外<强>有时LINQ不是答案。< /强>这是一个问题,我会考虑使用一个自定义比较器来使用<代码> Hasset < /Cord>,以减少执行SET操作的工作。哈希集在执行集合操作时比列表更高效,并且(取决于数据)可以大大减少工作量:
// 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
//创建一个可以容纳Alpha或Bravo的包装类
类ABItem{
公共对象实例{get;private set;}
public int Id{get;private set;}
公共ABItem(Alpha a){Instance=a;Id=a.Id;}
public ABItem(Bravo b){Instance=b;Id=b.Id;}
}
//按id比较Alphas和Bravos的比较器
类ABItemComparer:IComparer{
公共整数比较(对象a,对象b){
返回GetId(a).Compare(GetId(b));
}
私有int GetId(对象x){
如果(x是Alpha)返回((Alpha)x).Id;
如果(x是Bravo)返回((Bravo)x).Id;
抛出新的InvalidArgumentException();
}
}
//基于比较Abitem的ID创建比较器
var comparer=新的ABComparer();
var hashAlphas=
新HashSet(myAlphaItems.Select(x=>newabitem(x)),comparer);
var hashBravos=
新HashSet(myBravoItems.Select(x=>newabitem(x)),comparer);
//Alpha和Bravo集合中具有通用ID的项目:
var hashCommon=新的HashSet(hashAlphas).IntersectWith(hashSetBravo);
hashSetAlpha.ExceptWith(hashSetCommon);//仅Alpha格式的项目
hashSetBravo.ExceptWith(hashSetCommon);//项目只在布拉沃
这里有一个可能的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))
)或任何内容创建字典!这只是对项目进行分类的一种方式。我相信有更好的解决方案,但这似乎是一个真正有趣的问题,所以我想我不妨试一试 Dictionary alphaDictionary=myAlphaItems.ToDictionary(a=>a.Id);
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();
字典bravoDictionary=myBravoItems.ToDictionary(b=>b.Id);
ILookup keyLookup=字母字典.Keys
.Union(bravoDictionary.Keys)
.ToLookup(x=>字母词典.ContainsKey(x)?
(bravoDictionary.ContainsKey(x)?“两者”:“阿尔法”):
“好极了”);
列出alphaBoth=keyLookup[“both”]。选择(x=>alphaDictionary[x])。ToList();
列出bravoBoth=keyLookup[“both”]。选择(x=>bravoDictionary[x])。ToList();
列出alphaOnly=keyLookup[“alpha”]。选择(x=>alphaDictionary[x])。ToList();
List bravoOnly=keyLookup[“bravo”]。选择(x=>bravoDictionary[x])。ToList();
如果您想遍历和比较最短的时间,我认为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);
}
}
我认为对于HashSet,您需要一个
IEqualityComparer
而不是iccomparer
。而不是使用GetId方法,我只需要执行a.Id.CompareTo(b.Id)
(因为使用这个通用比较器,您将接收abitem而不是对象)。
public class AlphaAndBravo {
public Alpha a { get; set; }
public Bravo b { get; set; }
}