代码之家  ›  专栏  ›  技术社区  ›  Eric

匿名类型和两个列表的交集

  •  2
  • Eric  · 技术社区  · 15 年前
    public class thing
    {
    public int Id{get;set;}
    public decimal shouldMatch1 {get;set;}
    public int otherMatch2{get;set;}
    public string doesntMatter{get;set;}
    public int someotherdoesntMatter{get;set;}
    }
    List<thing> firstList = new List<thing>();
    List<thing> secondList = new List<thing>();
    
    firstList.Add( new thing{ Id=1,shouldMatch1 = 1.11M, otherMatch2=1000,doesntMatter="Some fancy string", someotherdoesntMatter=75868});
    firstList.Add( new thing{ Id=2,shouldMatch1 = 2.22M, otherMatch2=2000,doesntMatter="Some fancy string", someotherdoesntMatter=65345});
    firstList.Add( new thing{ Id=3,shouldMatch1 = 3.33M, otherMatch2=3000,doesntMatter="Some fancy string", someotherdoesntMatter=75998});
    firstList.Add( new thing{ Id=4,shouldMatch1 = 4.44M, otherMatch2=4000,doesntMatter="Some fancy string", someotherdoesntMatter=12345});
    
    secondList.Add( new thing{ Id=100,shouldMatch1 = 1.11M, otherMatch2=1000,doesntMatter="Some fancy string", someotherdoesntMatter=75868});
    secondList.Add( new thing{ Id=200,shouldMatch1 = 2.22M, otherMatch2=200,doesntMatter="Some fancy string", someotherdoesntMatter=65345});
    secondList.Add( new thing{ Id=300,shouldMatch1 = 3.33M, otherMatch2=300,doesntMatter="Some fancy string", someotherdoesntMatter=75998});
    secondList.Add( new thing{ Id=400,shouldMatch1 = 4.44M, otherMatch2=4000,doesntMatter="Some fancy string", someotherdoesntMatter=12345});
    //Select new firstList.Id,secondList.Id where firstList.shouldMatch1 ==secondList.shouldMatch1  && firstList.otherMatch2==secondList.otherMatch2
    
    //SHould return 
    //1,100
    //4,400
    

    是否有一种方法与列表相交,或者我必须迭代它们?

    伪代码

        firstList.Intersect(secondList).Where(firstList.shouldMatch1 == secondList.shouldMatch1 && firstList.otherMatch2 == secondList.otherMatch2)
    Select new {Id1=firstList.Id,Id2=secondList.Id};
    

    当做

    埃里克

    4 回复  |  直到 15 年前
        1
  •  4
  •   Ahmad Mageed    15 年前

    您可以使用除交叉和实现IEqualityComparer之外的方法,如下所示:

    var query = from f in firstList
                from s in secondList
                where f.shouldMatch1 == s.shouldMatch1 &&
                      f.otherMatch2 == s.otherMatch2
                select new { FirstId = f.Id, SecondId = s.Id };
    
    foreach (var item in query)
        Console.WriteLine("{0}, {1}", item.FirstId, item.SecondId);
    

    这本质上是 Enumerable.SelectMany method 以查询格式。加入可能比这种方法更快。

        2
  •  2
  •   kbrimington    15 年前

    考虑使用多条件联接来联接记录。交叉会使你在左边或右边丢失ID。

    下面是一个针对这个特定场景的工作多列联接的示例。此查询的吸引力在于它不需要相等比较器,并且允许您在连接其他指定列时检索ID列。

    var query = from first in firstList
                join second in secondList on
                    new { first.shouldMatch1, first.otherMatch2 }
                        equals
                    new { second.shouldMatch1, second.otherMatch2 }
                select new
                {
                    FirstId = first.Id,
                    SecondId = second.Id
                };
    
        3
  •  1
  •   Jon Skeet    15 年前

    你需要把你的 thing 类型重写 Equals GetHashCode 表示其相等语义:

    public sealed class Thing : IEquatable<Thing>
    {
        public int Id{get;set;}
        public decimal ShouldMatch1 {get;set;}
        public int OtherMatch2{get;set;}
        public string DoesntMatter{get;set;}
        public int SomeOtherDoesntMatter{get;set;}
    
        public override int GetHashCode()
        {
            int hash = 17;
            hash = hash * 31 + ShouldMatch1.GetHashCode() ;
            hash = hash * 31 + OtherMatch2.GetHashCode() ;
            return hash;
        }
    
        public override bool Equals(object other) {
            return Equals(other as Thing);
        }
    
        public bool Equals(Thing other) {
            if (other == null) {
                return false;
            }
            return ShouldMatch1 == other.ShouldMatch1 &&
                   OtherMatch2 == other.OtherMatch2;
        }
    }
    

    注意,密封类使相等性测试更简单。还请注意,如果您将其中一个作为键放在字典中,然后更改ID、shouldmatch1或othermatch2,您将无法再次找到它…

    如果你用的是 真实的 匿名类型,你不能这样做…而且很难实现 IEqualityComparer<T> 传递给 Intersect 当它是匿名的。你可以写一个 IntersectBy 方法,有点像Morelinq的 DisinctBy 方法…如果您真的使用匿名类型,这可能是最干净的方法。

    您可以这样使用它:

    var query = first.Intersect(second);
    

    你最后会得到一个 IEnumerable<Thing> 你可以从中得到正确的信息。

    另一种选择是使用联接:

    var query = from left in first
                join right in second 
                on new { left.ShouldMatch1, left.OtherMatch2 } equals
                   new { right.ShouldMatch1, right.OtherMatch2 }
                select new { left, right };
    

    (编辑:我刚刚注意到其他人也加入了…啊,好吧。)

    然而 另一个 如果您只对匹配的位感兴趣,可以选择投影序列:

    var query = first.Select(x => new { x.ShouldMatch1, x.OtherMatch2 })
                     .Intersect(second.Select(x => new { x.ShouldMatch1, 
                                                         x.OtherMatch2 }));
    
        4
  •  0
  •   AxelEckenberger    15 年前

    您需要一个相等比较器:

    public class thingEqualityComparer : IEqualityComparer<thing>
    {
        #region IEqualityComparer<thing> Members
    
        public bool Equals(thing x, thing y) {
            return (x.shouldMatch1 == y.shouldMatch1 && x.otherMatch2 == y.otherMatch2) 
    
        public int GetHashCode(thing obj) {
                // if this does not suffice provide a better implementation.
            return  obj.GetHashCode();
        }
    
        #endregion
    }
    

    然后,可以将集合与以下项相交:

    firstList.Intersect(secondList, new thingEqualityComparer());
    

    或者,可以重写 Equal 函数(参见约翰的解决方案)。

    也请不要那样 thing 不是匿名类-例如 new { prop = 1 } .