<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yannick Kroll</title>
    <description>The latest articles on DEV Community by Yannick Kroll (@pe4nut-dev).</description>
    <link>https://dev.to/pe4nut-dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4178634%2F7b7368ee-4779-42c7-a7c6-8f8579c8dac2.png</url>
      <title>DEV Community: Yannick Kroll</title>
      <link>https://dev.to/pe4nut-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pe4nut-dev"/>
    <language>en</language>
    <item>
      <title>Why Row-by-Row CSV Comparison Fails When Product Catalogs Get Reordered</title>
      <dc:creator>Yannick Kroll</dc:creator>
      <pubDate>Mon, 12 Oct 2026 07:19:44 +0000</pubDate>
      <link>https://dev.to/pe4nut-dev/why-row-by-row-csv-comparison-fails-when-product-catalogs-get-reordered-2pce</link>
      <guid>https://dev.to/pe4nut-dev/why-row-by-row-csv-comparison-fails-when-product-catalogs-get-reordered-2pce</guid>
      <description>&lt;p&gt;Imagine receiving an updated product catalog from a supplier.&lt;/p&gt;

&lt;p&gt;Both files contain product SKUs, names, prices, and stock quantities. You want to know which products were added, removed, or changed.&lt;/p&gt;

&lt;p&gt;A simple line-by-line comparison sounds reasonable. But there's a catch: &lt;strong&gt;the supplier may have reordered the rows without changing the products themselves.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's a small example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Original catalog&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SKU&lt;/th&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;000101&lt;/td&gt;
&lt;td&gt;Desk Tray&lt;/td&gt;
&lt;td&gt;18.90&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;000102&lt;/td&gt;
&lt;td&gt;Cable Keeper&lt;/td&gt;
&lt;td&gt;6.90&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;000103&lt;/td&gt;
&lt;td&gt;Notebook&lt;/td&gt;
&lt;td&gt;11.00&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;000104&lt;/td&gt;
&lt;td&gt;Mug&lt;/td&gt;
&lt;td&gt;14.90&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Updated catalog&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SKU&lt;/th&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;000103&lt;/td&gt;
&lt;td&gt;Notebook&lt;/td&gt;
&lt;td&gt;11.00&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;000102&lt;/td&gt;
&lt;td&gt;Cable Keeper&lt;/td&gt;
&lt;td&gt;7.50&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;000105&lt;/td&gt;
&lt;td&gt;Bottle Sleeve&lt;/td&gt;
&lt;td&gt;9.50&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;000101&lt;/td&gt;
&lt;td&gt;Desk Tray&lt;/td&gt;
&lt;td&gt;18.90&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you compare these files row by row, the first rows appear completely different.&lt;br&gt;
But the Notebook didn't become a Desk Tray. It simply moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution: Match records by SKU
&lt;/h2&gt;

&lt;p&gt;Instead of comparing row positions, use a stable identifier such as the SKU.&lt;/p&gt;

&lt;p&gt;A reliable key-based comparison follows four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse both CSV files and index their records by SKU.&lt;/li&gt;
&lt;li&gt;Check for missing or duplicate keys in each file.&lt;/li&gt;
&lt;li&gt;Match records using their SKU values, regardless of row order.&lt;/li&gt;
&lt;li&gt;Compare the remaining fields for matching records and classify unmatched records as added or removed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the example above, the actual results are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;SKU&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Changed&lt;/td&gt;
&lt;td&gt;000102&lt;/td&gt;
&lt;td&gt;Price: 6.90 → 7.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changed&lt;/td&gt;
&lt;td&gt;000103&lt;/td&gt;
&lt;td&gt;Stock: 80 → 60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Added&lt;/td&gt;
&lt;td&gt;000105&lt;/td&gt;
&lt;td&gt;New Bottle Sleeve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Removed&lt;/td&gt;
&lt;td&gt;000104&lt;/td&gt;
&lt;td&gt;Mug no longer present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unchanged&lt;/td&gt;
&lt;td&gt;000101&lt;/td&gt;
&lt;td&gt;No data changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's &lt;strong&gt;two changed products, one added, one removed, and one unchanged&lt;/strong&gt; — regardless of their order in either file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch out for leading zeros
&lt;/h2&gt;

&lt;p&gt;SKUs such as &lt;code&gt;000101&lt;/code&gt; are identifiers, not numbers.&lt;/p&gt;

&lt;p&gt;Converting &lt;code&gt;000101&lt;/code&gt; to a numeric value produces &lt;code&gt;101&lt;/code&gt;, which can break comparisons or create mismatches with other systems.&lt;br&gt;
Keep identifiers as strings throughout parsing and comparison.&lt;/p&gt;

&lt;p&gt;Also remember that spreadsheet programs may automatically reinterpret CSV values when opening them. A correctly exported CSV can still appear to have lost leading zeros if imported with incorrect column types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other common pitfalls
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Duplicate SKUs:&lt;/strong&gt; If multiple rows share the same matching key, a simple lookup can overwrite records. Detect duplicates and report them rather than silently selecting one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing SKUs:&lt;/strong&gt; Records without a usable key cannot be matched reliably. They need separate review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inconsistent formatting:&lt;/strong&gt; Whitespace and letter casing may cause unexpected mismatches. Decide explicitly whether values should be normalized before matching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Different columns:&lt;/strong&gt; If the supplier adds or removes a column, distinguish structural changes from actual product-value changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quoted CSV values:&lt;/strong&gt; CSV files can contain commas, quotes, and multiline fields. Use a proper CSV parser instead of splitting lines and commas manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;When comparing product catalogs, row position is rarely a reliable identity.&lt;/p&gt;

&lt;p&gt;A stable, unique key lets you separate real business changes from harmless row reordering.&lt;br&gt;
That makes the resulting difference report much more useful for checking prices, inventory, and product additions before updating another system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it with real CSV files
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://toolgrain.com/tools/csv-compare" rel="noopener noreferrer"&gt;Toolgrain CSV Compare&lt;/a&gt;, a browser-based tool that matches CSV records by a selected key and highlights added, removed, and changed rows.&lt;/p&gt;

&lt;p&gt;You can try it with fictional sample files without creating an account. CSV processing happens locally in your browser.&lt;br&gt;
If you have suggestions for improving the comparison workflow, I'd be interested in hearing them.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>data</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
