【问题标题】:Powershell: Compare two arrays, find missing items and changed valuesPowershell:比较两个数组,查找缺失项和更改值
【发布时间】:2021-07-08 21:03:10
【问题描述】:

我有一个问题,希望有人能够提供一些指导。 我有两个 csv 文件,File1 和 File2。

文件1:

value1,value2
A,10
B,30
C,45
D,39

文件2:

value1,value2
A,10
B,32
C,44
E,7
F,3

我正在寻找的是两件事。我需要检查文件之间是否删除了 value1 中的任何项目,和/或我需要检查相应的数字 value2 是否减少了。

首先,比较文件并找出在 File1 和 File2 中添加或删除了哪些行。如果我只比较 value1 项,那么使用 Compare-Object 就很容易了。

所以我第一次比较的结果,我会看到从 File1 到 File2,我会看到添加了 E 和 F 行,并且删除了 D 行。完美。

但是,这是我正在努力解决的下一部分。然后我需要比较每个文件中的 value2,并确定该数字是否减少(或可能增加或保持不变)。

棘手的部分是我不能只将 File1 中的第 4 行 value2 与 File2 中的第 4 行 value2 进行比较,因为一个用于 D,另一个用于 E。我不知道如何先匹配项目,然后基本上只取那些匹配的项目并比较那些项目的 value2?但是如果没有前一行匹配,会发生什么呢? (因为该行是新添加到file2或该行被删除并且只存在于file1中)

最后,我要提出的是所有已删除的 value1 的列表,以及自 File1 以来相应 value2 减少的所有 value1 的列表。我不关心增加或增加。

希望有人可以提供一些指导。 谢谢!

【问题讨论】:

  • 一段时间以来,我一直在维护Join-Object script/Join-Object Module(另请参阅:In Powershell, what's the best way to join two tables into one?)。对于您问题的第一部分,语法将类似于:$Csv1 |LeftJoin $Csv2 -on value1 -Name Old,New。对于问题的第二部分,您可以添加一个计算属性,例如:-Property *, @{Decreased = {$Right.value2 -lt $Left.value2}}
  • 或者只是:$Csv1 |LeftJoin $Csv2 -on value1 -Name Old,New |Where {$_.Newvalue2 -lt $_.Oldvalue2}

标签: powershell


【解决方案1】:

提供Santiago's helpful answer 的单管道替代方案:

它依赖于Add-MemberGroup-Objectcalculated properties,通过Select-Object

(@'
value1,value2
A,10
B,30
C,45
D,39
'@ | ConvertFrom-Csv | Add-Member -PassThru Source 1) + 
(@'
value1,value2
A,10
B,32
C,44
E,7
F,3
'@ | ConvertFrom-Csv | Add-Member -PassThru Source 2) | 
  Group-Object value1 | ForEach-Object {
    if ($_.Count -eq 1) {
      $_.Group[0] | Add-Member -PassThru Status @{ 1 = '<='; 2 = '=>' }[$_.Group[0].Source]
    }
    else {
      $grp = $_
      $change = ($_.Group[0].value2).CompareTo($_.Group[1].value2)
      $_.Group[0] |
        Select-Object value1,
                      @{ name = 'value2'; expression = { $grp.Group[0].value2, $grp.Group[1].value2 } },
                      @{ name = 'Status'; expression = { $change } }
      }
    }

以上结果如下:

value1 value2   Status
------ ------   ------
A      {10, 10}      0
B      {30, 32}     -1
C      {45, 44}      1
D      39           <=
E      7            =>
F      3            =>
  • &lt;==&gt;Compare-Object 输出中的含义相同:&lt;= 表示value1 是LHS 独有的值,=&gt; 是RHS 独有的值。

  • -1 / 0 / 1 映射到 value2 属性值的增加/相等/减少。

【讨论】:

  • 这太聪明了,我想都不敢想。我的大脑只是默认为经典问题,像这样的问题!我重新编写了代码以赋予数据更多意义。
  • 谢谢@SantiagoSquarzon。它当然是多个 cmdlet 的不明显组合。
【解决方案2】:

我决定对代码进行一些编辑,因为我认为您正在进行某种数据分析,我认为显示信息的最佳方式是合并两个对象。

可能需要更多的处理时间,具体取决于 CSV 的大小,但我认为这是显示数据的更简洁的方法,特别是如果要将其导出到 Excel,过滤结果对象将非常容易.

另外,mklement0's answer 比我聪明得多。我们可以说这可能是一种更经典的编码方法,并且他正在使用他可以使用的所有 PowerShell 工具集。对他的回答也有很大的帮助。

# Use Import-Csv here, this is just for testing
$csv1 = @'
value1,value2
A,10
B,30
C,45
D,39
'@ | ConvertFrom-Csv

$csv2 = @'
value1,value2
A,10
B,32
C,44
E,7
F,3
'@ | ConvertFrom-Csv

# Convert CSV1 to hashtable
$map = @{}

foreach($line in $csv1)
{
    $map.Add($line.value1,$line.value2)
}

$result = [system.collections.generic.list[pscustomobject]]::new()

$map.Keys.ForEach({
    if($_ -notin $csv2.value1)
    {
        $result.Add(
            [pscustomobject]@{
                value1 = $_
                oldval2 = $map[$_]
                newval2 = $null
                status = 'REMOVED'
        })
    }
})

foreach($line in $csv2)
{
    $out = [ordered]@{
        value1 = $line.value1
        newval2 = $line.value2
    }
    if(-not $map.ContainsKey($line.value1))
    {
        $out.oldval2 = $null
        $out.status = 'ADDED'
        $result.Add([pscustomobject]$out)
        
        continue
    }

    $out.oldval2 = $map[$line.value1]

    switch($line.value2)
    {
        {$_ -lt $map[$line.value1]}
        {
            $out.status = 'DECREASED'
            continue
        }
        {$_ -gt $map[$line.value1]}
        {
            $out.status = 'INCREASED'
            continue
        }
        Default
        {
            $out.status = 'EQUAL'
        }
    }

    $result.Add([pscustomobject]$out)
}

看着$result

PS /> $result

value1 oldval2 newval2 status   
------ ------- ------- ------   
D      39              REMOVED  
A      10      10      EQUAL    
B      30      32      INCREASED
C      45      44      DECREASED
E              7       ADDED    
F              3       ADDED    

如果您想查看 'REMOVED''DECREASED' 值:

PS /> $result.where({$_.status -match 'REMOVED|DECREASED'})

value1 oldval2 newval2 status   
------ ------- ------- ------   
D      39              REMOVED  
C      45      44      DECREASED

【讨论】:

  • 相信你完全理解了!现在我看到你写的东西是有道理的,但我不认为我会自己想出这个。一个后续问题,最后你的捕获值增加或保持与“增加”相同,就像“A”一样,分离相同值的最佳策略是什么?只需在最后一个 else 之前添加一个 elseif -gt 部分?
  • @tylergun 你是完全正确的,我错过了相等指针,而且我决定重新做孔代码。我希望这些数据现在更有意义。
  • 太好了,现在确实更有意义了,对于像我这样的初学者来说,您的解决方案绝对比@mklement0 的答案更容易理解和理解:) 但我非常感谢这两个例子!再次感谢。
  • 我很乐意帮助@tylergun
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-02
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
相关资源
最近更新 更多