在PowerShell中选择阵列的所有对象上的一个属性的值

时间:2011-03-03 04:55:08

标签: arrays powershell member-enumeration

假设我们有一个对象数组$ objects。假设这些对象具有“名称”属性。

这就是我想做的事情

 $results = @()
 $objects | %{ $results += $_.Name }

这有效,但可以更好的方式完成吗?

如果我这样做:

 $results = objects | select Name

$results是具有Name属性的对象数组。我希望$ results包含一个Names数组。

有更好的方法吗?

4 个答案:

答案 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,语法更简单,更快

    • 访问集合级别的属性以将其成员' 值作为数组称为 成员枚举 ,是PSv3+ feature
    • 或者,在 PSv2 中,使用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的回答的评论,但我还没有足够的声誉。)