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

时间:2010-09-14 17:40:46

标签: c# linq anonymous-types

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};

此致

_Eric

4 个答案:

答案 0 :(得分:4)

您可以使用除交叉和实现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。联接可能比这种方法更快。

答案 1 :(得分:2)

考虑使用多条件联接来加入记录。交叉会导致您在左侧或右侧丢失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
            };

答案 2 :(得分:1)

您需要使thing类型覆盖EqualsGetHashCode来表明其等同语义:

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>传递给{{1}是很棘手的当它是匿名的时候。您可以编写一个Intersect方法,有点像MoreLINQ的DisinctBy方法......如果您真的使用匿名类型,这可能是最干净的方法。

你会这样使用它:

IntersectBy

然后你得到一个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 };

答案 3 :(得分:0)

您需要一个相等比较器:

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函数(请参阅John的解决方案)。

另请注意,thing不是匿名课程 - 例如new { prop = 1 }