在PowerShell中过滤多维数组

时间:2018-03-27 23:44:32

标签: arrays powershell

我正在寻找关于过滤多维数组的一些建议,以及是否有更好的方法来覆盖我现有的方法。

我正在过滤一个多维数组,其中多个值可能是Null,“”,“”等等(即它们没有我确定的有效值)。该数组如下所示:

$Files.Path
$Files.Owner
$Files.Vendor
$Files.Company
$Files.Product
$Files.Description
$Files.ProductVersion
$Files.FileVersion

要过滤供应商,公司,产品,说明,我可以通过以下几种方式进行:

方法1:

$Values = @(" ", "", $Null)
$NoMetadata = $Files | Where-Object {
    ($Values -contains $_.Vendor) -and `
    ($Values -contains $_.Company) -and `
    ($Values -contains $_.Product) -and `
    ($Values -contains $_.Description)
}

方法2:

$NoMetadata = $Files | Where-Object { $_.Vendor -le 1 -and `
$_.Company -le 1 -and $_.Product -le 1 -and $_.Description -le 1 }

我很欣赏任何有关改进方法的建议。

2 个答案:

答案 0 :(得分:1)

我建议您定义一个帮助函数(使用方法2中更简单的-le 1条件,它会为$True返回$null""" "相似):

Function test-NoMetaData {
  param([object] $obj)
  # Loop over all property names and inspect that property's value on the input object.
  foreach ($propName in 'Vendor', 'Company', 'Product', 'Description') {
    if ($obj.$propName -le 1) { return $False }
  }
  return $True
}

# Filter $Files down to those objects that lack the (complete) metadata.
$filesWithoutMetaData = $Files | Where-Object { test-NoMetaData $_ }

您也可以将代码直接放在Where-Object块中,并直接引用$_

可选阅读:如果您想让功能更复杂,请继续阅读。

考虑实施一个Filter函数,您可以在管道中直接使用

Filter select-WithMetaData {
  param([switch] $NotPresent) #  To invert the logic
  if ($Args) { Throw "Unrecognized arguments: $Args" }
  if (-not $MyInvocation.ExpectingInput) { return } # no pipeline input; nothing to do.
  $haveAllMetaData = $True
  foreach ($propName in 'Vendor', 'Company', 'Product', 'Description') {
    if ($_.$propName -le 1) { $haveAllMetaData = $False; break }
  }
  # Pass the input object through only if it has/doesn't have the requisite metadata.
  if ($haveAllMetaData -ne $NotPresent) { $_ }
}

$filesWithoutMetaData = $Files | select-WithMetaData -NotPresent
$filesWithMetaData =    $Files | select-WithMetaData

Filter是简化的函数,可以更轻松地定义接受管道输入的功能:为{em>每个输入调用Filter函数的主体object $_引用该输入对象。

Filter函数很方便,但有缺点:

  • 您不能将输入作为直接参数传递,作为管道输入的替代方法(除非您明确定义管道绑定参数,这会使Filter提供的简化语法的优势无效。)
  • 在收到管道输入之前/之后,您无法运行初始化/清理代码。

使用Function语法来避免这些限制 - 请参阅下文。

编写 接受直接参数输入的功能支持常用参数(这使其成为 高级 function (cmdlet-like)),你必须使用Function构造并显式声明一个参数作为接受管道输入。

此外,您的函数必须有一个process { ... }块,为每个输入项调用;可选地,它可以具有begin {...}end { ... }块,用于管道前输入初始化/管道输入后清理。
警告:如果你使用process块,你的函数只会被调用一次,此时管道绑定参数变量仅包含 last 输入对象。

PSv3 +语法:

Function Select-WithMetaData {
  [CmdletBinding()] # Make this an advanced function with common-parameter support.
  param(
    # Declare -File as accepting a single file directly or multiple files via the pipeline.
    [Parameter(ValueFromPipeline, Mandatory)] [object] $File,
    [switch] $NotPresent
  )

  # Invoked once with a directly passed -File argument bound to $File,
  # and for each input object, also bound to $File, if used in the pipeline.
  process { 
    $haveAllMetaData = $True
    foreach ($propName in 'Vendor', 'Company', 'Product', 'Description') {
      if ($File.$propName -le 1) { $haveAllMetaData = $False; break }
    }
    if ($haveAllMetaData -ne $NotPresent) { $File }
  }

}

答案 1 :(得分:0)

稍微修改了这个功能,这看起来效果很好。感谢@ mklement0

Function Test-AcMetadata {
    [CmdletBinding(SupportsShouldProcess = $False)]
    [OutputType([Bool])]
    Param (
        [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $False)]
        [object]$obj
    )
    ForEach ($Property in 'Vendor', 'Company', 'Product', 'Description') {
        If ($obj.$Property -ge 2) { Return $True }
    }
    Return $False
}

可以过滤元数据或没有元数据:

$NoMetadata = $Files | Where-Object { (Test-AcMetadata $_) -eq $False }
$Metadata = $Files | Where-Object { Test-AcMetadata $_ }