Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.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# LINQ组合查询_C#_.net_Linq_Query Optimization - Fatal编程技术网

C# LINQ组合查询

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

我有两个不同类型的对象集合。让我们称他们为typeALPHA和typeBRAVO。每种类型都有一个属性,即对象的“ID”。类中没有重复的ID,因此对于任何给定ID,最多有一个ALPHA和一个BRAVO实例。我需要做的是将它们分为三类:

  • ALPHA中未出现在BRAVO集合中的ID实例
  • BRAVO中未出现在ALPHA集合中的ID实例
  • 出现在两个集合中的ID实例 在所有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));
    
    对于不匹配的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; }
        }