我正在寻找关于过滤多维数组的一些建议,以及是否有更好的方法来覆盖我现有的方法。
我正在过滤一个多维数组,其中多个值可能是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 }
我很欣赏任何有关改进方法的建议。
答案 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 $_ }