Reputation: 151
I am trying to compare two list of same type using multiple properties of that type.
For example,
I have a class named Details
public class Details
{
public int id;
public string symbol;
public string code;
}
I have below two lists:
List<Details> list1 = new List<Details>();
List<Details> list2 = new List<Details>();
list1.Add(new Details() { id=1,symbol="ANSI",code="NITE"});
list1.Add(new Details() { id = 1, symbol = "ANSI", code = "CALGO" });
list1.Add(new Details() { id = 1, symbol = "ANSI", code = "CANT" });
list1.Add(new Details() { id=2,symbol="ANSI",code="NITE"});
list1.Add(new Details() { id = 2, symbol = "ANSI", code = "CALGO" });
list1.Add(new Details() { id = 2, symbol = "ANSI", code = "CANT" });
list2.Add(new Details() { id = 1, symbol = "ANSI", code = "NITE" });
list2.Add(new Details() { id = 1, symbol = "ANSI", code = "CALGO" });
list2.Add(new Details() { id = 2, symbol = "ANSI", code = "NITE" });
I want only that data from List1 which has same id, symbol but different code.
So, in above scenario result will be as below.
list1.Add(new Details() { id = 1, symbol = "ANSI", code = "CANT" });
list1.Add(new Details() { id = 2, symbol = "ANSI", code = "CALGO" });
list1.Add(new Details() { id = 2, symbol = "ANSI", code = "CANT" });
It would be great if this can be achieved through Linq instead of using foreach.
I tried below but that's not correct.
var temp =list1.Where(x=>list2.Any(z=>x.id==z.id && string.Equals(x.symbol,z.symbol) && !string.Equals(x.code,z.code)));
Upvotes: 6
Views: 11742
Reputation: 50
Do this:
public class DetailsComparer : IEqualityComparer<Details>
{
public bool Equals(Details x, Details y)
=> x.id == y.id && x.symbol == y.symbol && x.code == y.code;
public int GetHashCode(Details obj)
=> obj.code.GetHashCode();
}
And use wery simple this
var x = list1.Except(list2, new DetailsComparer());
result x:
1, ANSI, CANT
2, ANSI, CALGO
2, ANSI, CANT
Upvotes: -1
Reputation: 116098
1) I would first override Equals
(and also GetHashCode
)
public class Details
{
public int id;
public string symbol;
public string code;
public override int GetHashCode()
{
return (id + symbol + code).GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as Details;
if (other == null) return false;
return id == other.id && symbol == other.symbol && code == other.code;
}
}
Then you can use Linq as
var result = list1.Except(list2).ToList();
It returns the result you expect...
2) Same result can also be obtained without changing the Details
object and by implementing a custom IEqualityComparer
public class DetailsComparer : IEqualityComparer<Details>
{
public bool Equals(Details x, Details y)
{
return x.id == y.id && x.symbol == y.symbol && x.code == y.code;
}
public int GetHashCode(Details obj)
{
return (obj.id + obj.symbol + obj.code).GetHashCode();
}
}
Then your linq would be
var result = list1.Except(list2, new DetailsComparer()).ToList();
Those ways are better than O(n*n) algorithms utilizing of Any
and All
Upvotes: 6
Reputation: 726479
It looks like you need rows to satisfy two conditions, not one, in order to make the output:
id
and symbol
, andid
, symbol
, and code
.Here is how to do that with LINQ directly:
var tmp = list1.Where(x=>
list2.Any(z=>x.id==z.id && x.symbol==z.symbol)
&& !list2.Any(z => x.id==z.id && x.symbol==z.symbol && x.code==z.code));
An alternative based on applying De Morgan's laws:
var tmp = list1.Where(x=>
list2.Any(z=>x.id==z.id && x.symbol==z.symbol)
&& list2.All(z => x.id!=z.id || x.symbol!=z.symbol || x.code!=z.code));
Upvotes: 7
Reputation: 36
of course, you can do compare like your code, but if you want your code are more structure you can override method Equals()
and Operator ==
:
public class Details
{
public int id;
public string symbol;
public string code;
public override bool Equals(System.Object obj)
{
if (obj == null) {
return false;
}
Details detail = obj as Details;
if ((System.Object) detail == null) {
return false;
}
return (id == detail.id) && (symbol == detail.symbol);
}
public bool Equals(other) {
return this.id == other.id && this.symbole == other.symbol;
}
public override int GetHashCode() {
return id ^ symbol.GetHashCode();
}
}
then you can compare two Detail object directly.
Upvotes: 0