假设我们有一个对象数组$ objects。假设这些对象具有“名称”属性。
这就是我想做的事情
$results = @()
$objects | %{ $results += $_.Name }
这有效,但可以更好的方式完成吗?
如果我这样做:
$results = objects | select Name
$results
是具有Name属性的对象数组。我希望$ results包含一个Names数组。
有更好的方法吗?
答案 0 :(得分:177)
我认为您可以使用ExpandProperty
的{{1}}参数。
例如,要获取当前目录的列表并显示Name属性,可以执行以下操作:
Select-Object
这仍然是返回DirectoryInfo或FileInfo对象。您可以通过管道到Get-Member(别名ls | select -Property Name
)来检查通过管道输入的类型。
gm
因此,要将对象扩展为您正在查看的属性类型,您可以执行以下操作:
ls | select -Property Name | gm
在您的情况下,您可以执行以下操作使变量成为字符串数组,其中字符串是Name属性:
ls | select -ExpandProperty Name
答案 1 :(得分:56)
作为一种更简单的解决方案,您可以使用:
$results = $objects.Name
哪个应该用$results
填充所有'名称' $objects
中元素的属性值。
答案 2 :(得分:16)
在何时使用哪种方法和效果比较的指导下,补充已有的有用答案。
外部,使用:
$objects.Name(PSv3 +),如下所示rageandqq's answer,语法更简单,更快。
foreach
语句,其输出也可以直接分配给变量:$results = foreach ($obj in $objects) { $obj.Name }
(Get-ChildItem).Name
),那么命令必须首先在结果数组元素之前运行可以访问。在管道 中,必须进一步处理结果,或者结果不适合整个内存,使用: 强>
$objects | Select-Object -ExpandProperty Name
-ExpandProperty
中解释了对$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
的需求。对于小型输入集合(数组),您可能不会注意到差异,特别是在命令行上,有时可以输入轻松命令更重要。
这是一个易于替代的,但 最慢的方法;它使用Scott Saad's answer(再次,PSv3 +): ;例如,以下PSv3 +解决方案很容易附加到现有命令:
# By property name (string):
$objects.ForEach('Name')
# By script block (much slower):
$objects.ForEach({ $_.Name })
为了完整起见:鲜为人知的 PSv4 + simplified ForEach-Object
syntax called an operation statement是另一种选择:
ForEach-Object
此方法类似于成员枚举,具有相同的权衡,但管道逻辑未应用; 略慢,但仍明显快于管道。
为了通过 name ( string 参数)提取单个属性值,此解决方案与成员枚举相同(尽管后者在语法上更简单)。
script-block 变体虽然慢得多,却允许任意转换;它是更快 - 一次性内存 - 替代基于管道的100,000
cmdlet 。
以下是基于 Command FriendlySecs (100-run avg.) Factor
------- --------------------------- ------
$objects.ForEach('Number') 0.078 1.00
$objects.Number 0.079 1.02
foreach($o in $objects) { $o.Number } 0.188 2.42
$objects | Select-Object -ExpandProperty Number 0.881 11.36
$objects.ForEach({ $_.Number }) 0.925 11.93
$objects | % { $_.Number } 1.564 20.16
$objects | % Number 2.974 38.35
个对象的输入集合的各种方法的示例时间,在100次运行中取平均值;绝对数字并不重要,并且根据许多因素而有所不同,但它应该让您了解相对的表现:
foreach
基于成员枚举/属性名称的集合方法解决方案比最快的基于管道的解决方案快10倍。
.ForEach({ ... }
声明解决方案慢了2.5,但仍然是最快管道解决方案的4-5倍。
将脚本块与收集方法解决方案(Select-Object -ExpandProperty
)一起使用会大大减慢速度,因此它几乎与最快的基于管道的解决方案相媲美(% Number
)。
ForEach-Object Number
(% Number
)表现最差,即使% { $_.Number }
概念等同于Time-Command
)。
测试的源代码:
注意:从.ForEach()
collection method下载函数$count = 1e5 # input-object count (100,000)
$runs = 100 # number of runs to average
# Create sample input objects.
$objects = 1..$count | % { [pscustomobject] @{ Number = $_ } }
# An array of script blocks with the various approaches.
$approaches = { $objects | Select-Object -ExpandProperty Number },
{ $objects | % Number },
{ $objects | % { $_.Number } },
{ $objects.ForEach('Number') },
{ $objects.ForEach({ $_.Number }) },
{ $objects.Number },
{ foreach($o in $objects) { $o.Number } }
# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Command, FriendlySecs*, Factor
以运行这些测试。
{{1}}
答案 3 :(得分:1)
注意,成员枚举仅在集合本身没有同名成员的情况下起作用。因此,如果您有一个FileInfo对象数组,则无法通过使用
获得一个文件长度数组 $files.length # evaluates to array length
在您说“显然很好”之前,请考虑一下。如果您有一个具有Capacity属性的对象数组,那么
$objarr.capacity
可以正常工作,除非 $ objarr实际上不是[Array],而是例如[ArrayList]。因此,在使用成员枚举之前,您可能必须先查看包含您的收藏的黑匣子。
(主持人的注意:这应该是对rageandqq的回答的评论,但我还没有足够的声誉。)