title: How do you create the correct Lambda expression to handle comparing 2 object lists?
body: <p>I have a <code>List&lt;imports&gt;</code> which is created by reading a CSV file.  I have a <code>List&lt;table&gt;</code> by reading from a database table.  What would be the correct way of setting up lambda expressions to:</p>
  
  <ul>
  <li>Find the intersection <em>(Records to UPDATE or Records with NO ACTION)</em></li>
  <li>Find the new items in List <em>(Records to INSERT)</em></li>
  <li>Find the items in List <strong>not</strong> in List <em>(Records to DELETE)</em></li>
  </ul>
  
  <p>Right now I am muscling my way through this like:</p>
  
  <pre><code>foreach (DTO.ImportData row in Helper.ImportTracker.ImportsValid)
  {
      bool isInsert = false;
      bool isUpdate = false;
      Model.Auto auto = null;
  
      // Get auto(s) for this SKU + VIN + ClientID...
      var autos = _dbFeed.Autoes.Where(a =&gt; a.StockNumber == row.Stock &amp;&amp; a.VIN == row.VIN &amp;&amp; a.ClientID == _targetClientID &amp;&amp; a.SourceClientID == _sourceClientID).ToList();
      if (autos.Count &gt; 1)        // ERROR...
      {
          Helper.ImportTracker.ImportsInvalid.Add(row);
          continue;
      }
      else if (autos.Count == 1)  // UPDATE...
      {
          auto = autos[0];
          if (auto.GuaranteedSalePrice != row.GuaranteedSalePrice ||
              auto.ListPrice != row.ListPrice ||
              auto.Miles != row.Miles ||
              auto.Active != row.Active ||
              auto.MSRP != row.MSRP ||
              auto.InternetPrice != row.Internet_Price ||
              auto.InvoiceCost != row.Invoice ||
              auto.Make != row.Make ||
              auto.Model != row.Model ||
              auto.Year != row.Year 
              )
          {
              Helper.ImportTracker.Updates.Add(row);
              isUpdate = true;
          }
          else
          {
              isUpdate = false;
              auto = null;
          }
      }
      else                        // INSERT...
      {
          isInsert = true;
          auto = new Model.Auto();
          _dbFeed.Autoes.AddObject(auto);
          Helper.ImportTracker.Inserts.Add(row);
      }
  
      // Fill in the data...
      if (auto != null)
      {
          ...
      }
      // left out for readability - this section just maps the import 
      // data to the table row and saves to the DB...
  }
  </code></pre>
  
  <p>The above section handles the first 2 cases I listed at the beginning.</p>
  
  <p>I am having a dickens of a time wrapping my head around the correct way to put lambdas together for this.  </p>
  
  <p>I realize I may have to convert all of my <code>List&lt;import&gt;</code> to <code>List&lt;table&gt;</code> so that I can compare apples to apples and that is not a problem.  I am also thinking I need to write a custom comparer along the lines of:</p>
  
  <pre><code>class TableComparer : IEqualityComparer&lt;table&gt;
  {
      public bool Equals(table x, table y)
      {
          if (Object.ReferenceEquals(x, y)) return true;
  
          if (Object.ReferenceEquals(x, null) ||
              Object.ReferenceEquals(y, null))
                  return false;
  
              return x.SKU == y.SKU &amp;&amp; x.VIN == y.VIN &amp;&amp; x.ClientID == y.ClientID;
      }
  
      public int GetHashCode(table table)
      {
          if (Object.ReferenceEquals(table, null)) return 0;
  
          int hashSKU = SKU == null ? 0 : SKU.GetHashCode();
          int hashVIN = VIN == null ? 0 : VIN.GetHashCode();
          int hashClientID = ClientID.GetHashCode();
  
          return hashClientID ^ hashSKU ^ hashVIN;
      }
  }
  </code></pre>
  
  <p>Then I can do:</p>
  
  <pre><code>var UpdateAutos = autos.Intersect(new TableComparer(imports));
  var InsertAutos = imports.Except(new TableComparer(autos));
  var DeleteAutos = autos.Except(new TableComparer(imports));
  </code></pre>
  
  <p>And now my head is spinning!  ;)</p>
  
  <p>Am I on the right track?  </p>
  
  <hr>
  
  <p>ADDITIONAL INFO:
  So far I am this far with my new code:</p>
  
  <pre><code>private void HandleAutos()
  {
      // convert to List&lt;auto&gt;...
      List&lt;Model.Auto&gt; imports = AutoConvert.Convert(Helper.ImportTracker.ImportsValid, _targetClientID, _sourceClientID, DateTime.UtcNow, _dbFeed);
  
      // get all DB records in List&lt;auto&gt;...
      List&lt;Model.Auto&gt; current = _dbFeed.Autoes.Where(a =&gt; a.ClientID == _targetClientID &amp;&amp; a.Active == true).ToList();
  
      // isolate all Inserts, Updates and Deletes...
      var intersect = imports.Intersect(current, new AutoIsIn());         // should be all autos with matching VIN &amp; SKU  //
      var updates = intersect.Intersect(current, new AutoHasChanged());   // should be a subset of changed resords        //
      var inserts = imports.Except(current, new AutoIsIn());              // should be all the imports not in the DB      //
      var deletes = current.Except(imports, new AutoIsIn());              // should be all the DB records not in imports  //
  
  }
  </code></pre>
  
  <p>And my Comparer class looks like:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  
  namespace RivWorks.FeedHandler.Library
  {
      class AutoIsIn : IEqualityComparer&lt;Model.Auto&gt;
      {
          public bool Equals(Model.Auto x, Model.Auto y)
          {
              if (Object.ReferenceEquals(x, y)) return true;
              if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false;
  
              return x.StockNumber == y.StockNumber &amp;&amp; x.VIN == y.VIN;
          }
  
          public int GetHashCode(Model.Auto auto)
          {
              if (Object.ReferenceEquals(auto, null)) return 0;
  
              int hashSKU = auto.StockNumber == null ? 0 : auto.StockNumber.GetHashCode();
              int hashVIN = auto.VIN == null ? 0 : auto.VIN.GetHashCode();
  
              return hashSKU ^ hashVIN;
          }
      }
  
      class AutoHasChanged : IEqualityComparer&lt;Model.Auto&gt;
      {
          public bool Equals(Model.Auto x, Model.Auto y)
          {
              if (Object.ReferenceEquals(x, y)) return true;
              if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false;
  
              return (x.GuaranteedSalePrice != y.GuaranteedSalePrice 
                   || x.ListPrice != y.ListPrice 
                   || x.Miles != y.Miles 
                   || x.MSRP != y.MSRP 
                   || x.InternetPrice != y.InternetPrice 
                   || x.InvoiceCost != y.InvoiceCost 
                   || x.Make != y.Make 
                   || x.Model != y.Model 
                   || x.Year != y.Year
                   );
          }
  
          public int GetHashCode(Model.Auto auto)
          {
              if (Object.ReferenceEquals(auto, null)) return 0;
  
              int hashMake = auto.Make == null ? 0 : auto.Make.GetHashCode();
              int hashModel = auto.Model == null ? 0 : auto.Model.GetHashCode();
              int hashYear = auto.Year.GetHashCode();
  
              int hashGSP = auto.GuaranteedSalePrice.GetHashCode();
              int hashLP = !auto.ListPrice.HasValue ? 0 : auto.ListPrice.GetHashCode();
              int hashMiles = !auto.Miles.HasValue ? 0 : auto.Miles.GetHashCode();
              int hashMSRP = !auto.MSRP.HasValue ? 0 : auto.MSRP.GetHashCode();
              int hashIP = !auto.InternetPrice.HasValue ? 0 : auto.InternetPrice.GetHashCode();
              int hashIC = !auto.InvoiceCost.HasValue ? 0 : auto.InvoiceCost.GetHashCode();
  
              return hashMake ^ hashModel ^ hashYear ^ hashGSP ^ hashLP ^ hashMiles ^ hashMSRP ^ hashIP ^ hashIC;
          }
      }
  }
  </code></pre>
  
  <p>Anything amiss so far?</p>
  
  <p>-kb</p>