3

how can i compare 2 lists and have the not matching items but according to the specifics properties

public partial class Cable : StateObject
{            
    public int Id { get; set; }
    public int CablePropertyId { get; set; }
    public int Item { get; set; }
    public int TagNo { get; set; }
    public string GeneralFormat { get; set; }
    public string EndString { get; set; }
    public string CableRevision { get; set; }         
}

I want to comparision accomplished accoring to the CablePropertyId,TagNo and CableRevision, if i use

var diffCables = sourceCables.Except(destinationCables).ToList();

the whole properties are compared to each other . how can i do that?

nnmmss
  • 2,850
  • 7
  • 39
  • 67

6 Answers6

4

Use Linq except method with custom EqualityComparer.

http://msdn.microsoft.com/en-us/library/bb336390(v=vs.110).aspx

class CableComparer : IEqualityComparer<Cable>
{
    public bool Equals(Cable x, Cable y)
    {
        return (x.CablePropertyId == y.CablePropertyId && ...);
    }

    public int GetHashCode(Cable x) // If you won't create a valid GetHashCode based on values you compare on, Linq won't work properly
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + x.CablePropertyID;
            hash = hash * 23 + ...
        }
        return hash;
    }
}

var diffCables = sourceCables.Except(destinationCables, new CableComparer());

Also, ToList() operation on the result isn't really necessary. Most of the time you can just operate on the result of Linq query IEnumerable without specifying the exact type; this way you won't waste performance on unneeded ToList() operation.

By the way, a couple of others proposed Where-based queries with simple lambda. Such solution is easier to read (in my opinion), but it's also less optimized: it forces n^2 checks, while IEqualityComparer allows Linq to be more optimal because of GetHashCode() method. Here's a great answer on importance of GetHashCode, and here's a great guide on writing GetHashCode() override.

Community
  • 1
  • 1
Max Yankov
  • 12,551
  • 12
  • 67
  • 135
1

You can create your own IEqualityComparer<Cable> like this:

public class CableComparer : IEqualityComparer<Cable>
{
    public bool Equals(Cable x, Cable y)
    {
        return x.CablePropertyId == y.CablePropertyId &&
               x.TagNo == y.TagNo &&
               x.CableRevision == y.CableRevision;
    }

    // If Equals() returns true for a pair of objects  
    // then GetHashCode() must return the same value for these objects. 
    public int GetHashCode(Cable x)
    {
        return x.CablePropertyId ^ 
               x.TagNo.GetHashCode() ^ 
               x.CableRevision.GetHashCode();
    }
}

Then use this overload of Except:

var comparer = new CableComparer();
var diffCables = sourceCables.Except(destinationCables, comparer).ToList();

Alternatively, the MoreLINQ library (also available on NuGet) provides a convenient ExceptBy method:

var diffCables = sourceCables.ExceptBy(
        destinationCables, 
        x => new { 
            x.CablePropertyId, 
            x.TagNo,
            x.CableRevision
        })
    .ToList();
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • I don't think that bitwise xor is a good way to write `GetHashCode()`. You'll get a collision on {1,2,4} and {2,1,4}, which seems a very likely combination. – Max Yankov Nov 05 '13 at 13:54
0

You can override the Equals and GetHashCode methods of Cable if you will always compare this object in this manner.

Otherwise you can write a custom comparer and use the overload for .Except

List.Except Method

David Pilkington
  • 13,528
  • 3
  • 41
  • 73
0

If you use LINQ with IQueryable<>, there may be solution with Where()

var destinationCablesAnon = destinationCables.Select(a=>new {a.CablePropertyId, a.TagNo ,a.CableRevision}); // add ToArray() if use IEnumerable<>

var diffCables  = sourceCables.Where(a=>!destinationCables.Contains(new {a.CablePropertyId, a.TagNo ,a.CableRevision})).ToList();
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
Vladimir
  • 2,082
  • 1
  • 13
  • 27
0

Essentially, when you want to compare your own types, you'll need to describe how they compare/differ from each other. Linq wouldn't know which properties in your Cable class are different right?

So you build a comparer which can be used generally to compare two types.

In this case, two Cable instances:

class CableComparer : IEqualityComparer<Cable>
{

    public bool Equals(Cable c1, Cable c2)//these represent any two cables.
    {
        if (c1.Height == c2.Height && ...)
        {
            return true;
        }
        else
        {
            return false;
        }
    }


    public int GetHashCode(Cable c)
    {
        //this will work if each ID is unique
        return c.Id.GetHashCode();
        //otherwise you do this:
        //return (c.Id ^ c. CablePropertyId).GetHashCode();
    }
}

Then:

IEnumerable<Cable> except =
            sourceCables.Except(destinationCables, new CableComparer());
gideon
  • 19,329
  • 11
  • 72
  • 113
0

I think you can use something like this:

sourceCables.Where(sc => !destinationCables.Any(dc => dc.CablePropertyId == sc.CablePropertyId && ...));
MRB
  • 3,752
  • 4
  • 30
  • 44
  • I think that while lambda-based approach is more readable, it can be worse performance-wise, as it won't be able to use optimizations based on GetHashCode in IEqualityComparer. – Max Yankov Nov 05 '13 at 13:43