user3458219
user3458219

Reputation: 151

C# Compare two list of same object type

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

Answers (4)

Ivan Mokrov
Ivan Mokrov

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

L.B
L.B

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

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

It looks like you need rows to satisfy two conditions, not one, in order to make the output:

  • There needs to be a match on id and symbol, and
  • There must be no match on id, 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));

Demo.

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

Demo.

Upvotes: 7

Daniel Zhang
Daniel Zhang

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

Related Questions