开发者

Anonymous type and intersection of 2 lists

开发者 https://www.devze.com 2023-01-16 07:26 出处:网络
public class thing { public int Id{get;set;} public decimal shouldMatch1 {get;set;} public int otherMatch2{get;set;}
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{ 开发者_Python百科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

Is there a way to intersect the lists, or must I iterate them?

Pseudocode

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

Regards

_Eric


You could use an approach other than intersecting and implementing an IEqualityComparer, as follows:

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

This is essentially the Enumerable.SelectMany method in query format. A join would likely be quicker than this approach.


Consider using a multi-condition join to join your records. An intersect would cause you to lose ID's either on the left or the right.

Here is an example of a working multi-column join for this particular scenario. The appeal of this query is that it requires no equality comparer, and it allows you to retrieve the ID column while joining on the other specified columns.

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


You need to make your thing type override Equals and GetHashCode to indicate its equality semantics:

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

Note that sealing the class makes the equality test simpler. Also note that if you put one of these in a dictionary as a key but then change Id, ShouldMatch1 or OtherMatch2 you won't be able to find it again...

Now if you're using a real anonymous type, you don't get to do this... and it's tricky to implement an IEqualityComparer<T> to pass to Intersect when it's anonymous. You could write an IntersectBy method, a bit like MoreLINQ's DisinctBy method... that's probably the cleanest approach if you're really using an anonymous type.

You'd use it like this:

var query = first.Intersect(second);

You then end up with an IEnumerable<Thing> which you can get the right bits out of.

Another option is to use a join:

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

(EDIT: I've just noticed others have done a join too... ah well.)

Yet another option if you're only interested in the bits of the match is to project the sequences:

var query = first.Select(x => new { x.ShouldMatch1, x.OtherMatch2 })
                 .Intersect(second.Select(x => new { x.ShouldMatch1, 
                                                     x.OtherMatch2 }));


You will need an equality comparer:

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
}

Then you can intersect the collections with:

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

Alternatively, you can override the Equal function (see John's solution).

Also please not that thing is not anonymous class - this would be for example new { prop = 1 }.

0

精彩评论

暂无评论...
验证码 换一张
取 消