Arrays 选择PowerShell中数组的所有对象的一个属性值
假设我们有一个对象数组$objects。假设这些对象具有“Name”属性 这就是我想做的Arrays 选择PowerShell中数组的所有对象的一个属性值,arrays,powershell,member-enumeration,Arrays,Powershell,Member Enumeration,假设我们有一个对象数组$objects。假设这些对象具有“Name”属性 这就是我想做的 $results = @() $objects | %{ $results += $_.Name } 这是可行的,但能用更好的方式吗 如果我这样做: $results = objects | select Name $results是具有Name属性的对象数组。我希望$results包含一个名称数组 有更好的方法吗?我想您可以使用选择对象的ExpandProperty参数 例如,要获取当前目录的列
$results = @()
$objects | %{ $results += $_.Name }
这是可行的,但能用更好的方式吗
如果我这样做:
$results = objects | select Name
$results
是具有Name属性的对象数组。我希望$results包含一个名称数组
有更好的方法吗?我想您可以使用
选择对象的ExpandProperty
参数
例如,要获取当前目录的列表并仅显示Name属性,可以执行以下操作:
ls | select -Property Name
ls | select -ExpandProperty Name
这仍然返回DirectoryInfo或FileInfo对象。您始终可以通过管道(别名gm
)检查通过管道的类型
因此,要将对象扩展为您正在查看的属性类型,可以执行以下操作:
ls | select -Property Name
ls | select -ExpandProperty Name
在您的情况下,只需执行以下操作即可将变量设置为字符串数组,其中字符串是Name属性:
$objects = ls | select -ExpandProperty Name
作为更简单的解决方案,您可以使用:
$results = $objects.Name
它应该用$objects
中元素的所有“Name”属性值数组填充$results
,以补充先前存在的有用答案,并提供何时使用哪种方法以及性能比较的指导
- 在管道[1]之外,使用(PSv3+):
- 如果首先从内存中的(管道)命令收集所有输出是可行的,还可以将管道与成员枚举相结合;e、 g:
(Get-ChildItem -File | Where-Object Length -lt 1gb).Name
- 权衡:
- 输入收集和输出数组必须作为一个整体装入内存。
- 如果输入集合本身是命令(管道)的结果(例如,
(Get ChildItem).Name
),则必须先运行该命令,直到完成,然后才能访问生成的数组元素
在管道中,如果必须将结果传递给另一个命令,特别是如果原始输入不能作为一个整体放入内存,请使用:
$objects | Select-Object -ExpandProperty Name
在中更全面地讨论的PSv4+是另一个性能良好的备选方案,但请注意,它要求首先收集内存中的所有输入,就像成员枚举一样:
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
- 此方法与成员枚举类似,具有相同的权衡,只是不应用管道逻辑;它比成员枚举稍微慢一些,尽管仍然明显快于管道
>P>通过名称(字符串参数)提取单个属性值,此解决方案与成员枚举(尽管后者在语法上更简单)相同。
脚本块变量({…}
)允许任意转换;它比基于管道的cmdlet(%
) 更快—一次全部在内存中—可供选择
注意:.ForEach()
数组方法与its(内存中的等效对象)一样,总是返回集合(的实例),即使只生成一个输出对象。
相比之下,成员枚举、Select Object
、ForEach Object
和Where Object
按原样返回单个输出对象,而不将其包装到集合(数组)中
比较各种方法的性能
以下是各种方法的样本计时,基于10000
对象的输入集合,平均10次运行;绝对数字并不重要,并且会根据许多因素而变化,但它应该让您感觉到相对性能(计时来自单核Windows 10虚拟机:
重要
- 相对性能取决于输入对象是常规.NET类型的实例(例如,作为
Get ChildItem
的输出)还是[pscustomobject]
实例(例如,作为Convert FromCsv
的输出)。
原因是[pscustomobject]
属性由PowerShell动态管理,它可以比(静态定义的)常规.NET类型的常规属性更快地访问它们。下面将介绍这两种情况
- 这些测试使用完整集合中已存在的内存作为输入,以便关注纯属性提取性能。使用流式cmdlet/函数调用作为输入,性能差异通常不会太明显,因为在该调用中花费的时间可能占所花费时间的大部分
- 为简洁起见,别名
%
用于ForEach对象
cmdlet
一般结论,适用于常规.NET类型和[pscustomobject]
输入:
- 成员枚举(
$collection.Name
)和foreach($collection中的obj)
解决方案是目前最快的,比最快的基于管道的解决方案快10倍或更多
- 令人惊讶的是,
%Name
的性能比%{$\u.Name}
差得多-请参阅
- PowerShell核心在这方面始终优于Windows PowerShell
常规.NET类型的计时:
- PowerShell核心版本7.0.0-preview.3
系数命令秒(平均运行10次)
------ ------- ------------------
1.00$objects.Name 0.005
1.06 foreach($o,单位为$obj)
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex
$count = 1e4 # max. input object count == 10,000
$runs = 10 # number of runs to average
# Note: Using [pscustomobject] instances rather than instances of
# regular .NET types changes the performance characteristics.
# Set this to $true to test with [pscustomobject] instances below.
$useCustomObjectInput = $false
# Create sample input objects.
if ($useCustomObjectInput) {
# Use [pscustomobject] instances.
$objects = 1..$count | % { [pscustomobject] @{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } }
} else {
# Use instances of a regular .NET type.
# Note: The actual count of files and folders in your file-system
# may be less than $count
$objects = Get-ChildItem / -Recurse -ErrorAction Ignore | Select-Object -First $count
}
Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..."
# An array of script blocks with the various approaches.
$approaches = { $objects | Select-Object -ExpandProperty Name },
{ $objects | % Name },
{ $objects | % { $_.Name } },
{ $objects.ForEach('Name') },
{ $objects.ForEach({ $_.Name }) },
{ $objects.Name },
{ foreach($o in $objects) { $o.Name } }
# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Factor, Command, Secs*
$files.length # evaluates to array length
$objarr.capacity