加快与CSV文件的Where-Object比较

时间:2018-08-17 12:41:36

标签: powershell powershell-v4.0

有没有一种方法可以提高Where-Obeject的比较。在两个文件中都有1000条记录的情况还可以,但是当文件超过500k时,记录的速度确实很慢。

function progressBar ($i, $totalItems) {
    Write-Progress -Activity "My Progress Bar" -Status "Doing stuff on $s" -PercentComplete ($i / $totalItems * 100)
}

$PSDefaultParameterValues['*:Encoding'] = 'utf8'

$f1 = (Import-Csv 'A.txt' -Delimiter 'µ' -Header samname, id)
$f2 = (Import-Csv 'B.txt' -Delimiter 'µ' -Header samname, id)

$counter = 0
$totalItems = $f1.Count

$f1 | ForEach-Object {
    $samname = $_.samname
    if ($m = $f2 | Where-Object {$_.samname -eq $samname}) {
        $_.id = $m.id
    } else {
        $_.id = $_.id
    }

    $counter += 1
    #Start-Sleep -s 3
    progressbar -i $counter -totalItems $totalItems
} 
$f1 | Export-Csv 'D.txt' -NoType

4 个答案:

答案 0 :(得分:5)

通过Where-Object语句中的ForEach-Object子句,您正在有效地创建一个嵌套循环,这意味着该脚本进行了2500亿次比较

我通常要做的是将数组之一转换为哈希表,其中的键是您要比较的属性的值,然后将其用作“查找表”。

与遍历整个数组相比,通过键访问哈希表的速度超级快,因此比您当前的方法要快得多:

$f1=(import-csv 'A.txt' -Delimiter 'µ' -header samname,id)
$f2=(import-csv 'B.txt' -Delimiter 'µ' -header samname,id)

$h2 = @{}
$f2 |ForEach-Object {
  $h2[$_.samname] = $_
}

$f1 |Foreach-object{
  $samname=$_.samname
  if($h2.ContainsKey($samname)){
    $_.id = $h2[$samname].id
  }

  $counter += 1
  #Start-Sleep -s 3
  progressbar -i $counter  -totalItems $totalItems
}

答案 1 :(得分:2)

正如Mathias R.Jessen所说,您要为第一组中的每条记录重复一次第二记录集。应该用更快的算法代替-如果对记录集进行排序,则使用哈希联接或合并联接。其次,Write-Progress方法非常慢。尽量不要在每次迭代时都更新进度。您可以尝试此优化版本:

#demo data
(1..100000 | % { "Name_$($_)µ$($_)" }) -join "`n" | out-file A.txt
(1..100000 | % { "Name_$($_)µ$($_)" }) -join "`n" | out-file B.txt

$f1=(import-csv 'A.txt' -Delimiter 'µ' -header samname,id)
$f2=(import-csv 'B.txt' -Delimiter 'µ' -header samname,id)

$dict = @{}
$f2 | % {$dict[$_.samname] = $_.id}

$f1| % {
  if ($dict.ContainsKey($_.samname)){
    $_.id=$dict[$_.samname].id
  } else {
    $_.id = $_.id
  }
  #update every 100 iterations
  if ($counter++%100 -eq 0) { progressbar -i $counter  -totalItems $totalItems }
}

答案 2 :(得分:0)

如果您试图在两个具有相同“ samname”属性的csv文件之间获取对象,则可能需要考虑使用compare-object,例如:

比较对象-引用对象$ f1-差异对象$ f2-属性samname -IncludeEqual

如果您想弄乱管道上的这些对象,请使用-passthru

答案 3 :(得分:0)

无论如何您都违反了PowerShell pipeline技术,我会考虑使用.Where({...})(PSv4 +)方法,该方法占用大量内存,但通常速度更快。

如果您确实想快速连接对象,则可以考虑使用Linq,如RamblingCookieMonster的https://github.com/ili101/Join-Object示例一样。