【问题标题】:How to compare two CSV files with different header rows and remove duplicates from the first one?如何比较具有不同标题行的两个 CSV 文件并从第一个中删除重复项?
【发布时间】:2020-07-20 02:14:25
【问题描述】:

我有两个 CSV 文件。第一个包含以下标题和数据:

Name,Email,OfficePhone
Bill,Bill@jump.com,123-456-7890

第二个只包含:

primaryEmail
Bill@jump.com

我想比较两者并从第一个文件中删除所有重复行,其中第一个文件中的电子邮件存在于第二个文件中。我正在尝试使用compare-object,但不确定从这里去哪里。

$File1 = Import-Csv C:\it\newuser.csv
$File2 = Import-Csv C:\it\email.csv

Compare-Object $File1 $File2 -Property email

【问题讨论】:

    标签: powershell csv


    【解决方案1】:

    最简单的方法可能是首先从第二个 CSV 中获取主要电子邮件,然后使用 Where-Object 从第一个 CSV 中过滤掉包含重复电子邮件的重复行。

    # Get primary emails from 2nd CSV
    $csv2 = (Import-Csv -Path .\2.csv).primaryEmail
    
    # Remove rows from 1st csv that don't have an email in $csv2
    $removedDuplicateRows = Import-Csv -Path .\1.csv | Where-Object {$_.Email -notin $csv2}
    
    # Export filtered rows into output.csv
    $removedDuplicateRows | Export-Csv -Path .\output.csv -NoTypeInformation
    

    如果您的标题列周围有引号,那么您需要包含这些引号(例如,.primaryEmail 变为 ."primaryEmail")。

    如果您使用的是 PowerShell 7,则可以使用 Export-Csv 中的 -UseQuotes Never 在输出 CSV 中不包含引号。

    【讨论】:

      【解决方案2】:

      RoadRunner's helpful answer 提供了一个有效的解决方案。

      但是,对于大型输入集性能可能会成为一个问题,因为对每个 CSV 输入行执行电子邮件地址数组 ($csv2) 的线性搜索.

      使用System.Collections.Generic.HashSet<T> 提供了一个解决方案,因为在哈希集中的查找速度始终如一。

      System.Linq.Enumerable.ToHashSet() 方法提供了一种方便的方法,可以从实现 System.Collections.Generic.IEnumerable<T> 的对象(例如数组)构造此类哈希集。

      # Build a case-insensitive hash set of email addresses from $File2 
      # whose elements are to be excluded from $File1.
      # Note that the cast to [string[]] is required in order for PowerShell
      # to find the right generic method overload.
      $refEmailsHashSet = [Linq.Enumerable]::ToHashSet(
        [string[]] (Import-Csv $File2).primaryEmail,
        [StringComparer]::CurrentCultureIgnoreCase
      )
      
      # Import $File1 and filter out the email addresses from $File2
      # Pipe to `Export-Csv -NoTypeInformation -Encoding ...` to save to a new CSV file.
      Import-Csv $File1 | Where-Object { -not $refEmailsHashSet.Contains($_.Email) }
      

      从 PowerShell 7.2 开始,另一种提高性能的方法(有点模糊)是解决Where-Object(和ForEach-Object)cmdlet 的低效实现,如GitHub issue #10982 中所述

      # Works the same as above, with faster alternative to Where-Object
      Import-Csv $File1 | 
        & { process { if (-not $refEmailsHashSet.Contains($_.Email)) { $_ } } }
      

      请注意,这种独立优化同样可以应用于 RoadRunner 答案中的线性查找 -notin 解决方案,对整体性能的影响最大。

      【讨论】:

      • 根据您的经验,在这里多少才算“大”?千?成千上万?等等?
      • @TylerH,考虑到包括硬件速度在内的许多变量,这很难量化。组合的行数越高(尤其是在文件 2 中),此解决方案实现的性能改进就越大。相反,对于小行数,优化可能会减慢速度(但这绝对不重要)。对 100(文件 2)/10,000(文件 1)行(每行 3 列)进行快速测试,在我的 Mac 上实现了 25% 的加速。 YMMV。另请注意我添加到答案中的其他优化(可以独立应用)。
      猜你喜欢
      • 1970-01-01
      • 2023-03-09
      • 2021-08-09
      • 2021-12-08
      • 2011-04-05
      • 1970-01-01
      • 2020-10-16
      • 1970-01-01
      相关资源
      最近更新 更多