简体   繁体   中英

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

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.

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM