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 }
.
精彩评论