Digamos que temos uma matriz de objetos $ objects. Digamos que esses objetos tenham uma propriedade "Nome".
Isto é o que eu quero fazer
$results = @()
$objects | %{ $results += $_.Name }
Isso funciona, mas pode ser feito de uma maneira melhor?
Se eu fizer algo como:
$results = objects | select Name
$results
é uma matriz de objetos com uma propriedade Name. Eu quero que $ results contenham uma matriz de nomes.
Existe uma maneira melhor?
arrays
powershell
member-enumeration
Sylvain Reverdy
fonte
fonte
$results = @($objects | %{ $_.Name })
. Às vezes, pode ser mais conveniente digitar na linha de comando, embora eu ache a resposta de Scott geralmente melhor.$objects | % Name
Respostas:
Eu acho que você pode ser capaz de usar o
ExpandProperty
parâmetro deSelect-Object
.Por exemplo, para obter a lista do diretório atual e apenas exibir a propriedade Name, faça o seguinte:
Isso ainda está retornando objetos DirectoryInfo ou FileInfo. Você sempre pode inspecionar o tipo que está passando pelo pipeline canalizando para Get-Member (alias
gm
).Portanto, para expandir o objeto para ser o tipo de propriedade que você está vendo, você pode fazer o seguinte:
No seu caso, você pode fazer o seguinte para que uma variável seja uma matriz de strings, onde as strings são a propriedade Name:
fonte
Como uma solução ainda mais fácil, você pode simplesmente usar:
O qual deve preencher
$results
com uma matriz de todos os valores da propriedade 'Nome' dos elementos$objects
.fonte
Exchange Management Shell
. Quando usando o Exchange precisamos usar$objects | select -Property Propname, OtherPropname
Para complementar as respostas preexistentes e úteis, com orientações sobre quando usar qual abordagem e uma comparação de desempenho .
Fora de um pipeline, use (PSv3 +):
como demonstrado na resposta de rageandqq , que é sintaticamente mais simples e muito mais rápida .foreach
instrução cuja saída você também pode atribuir diretamente a uma variável:(Get-ChildItem).Name
), esse comando deverá primeiro ser executado até a conclusão antes que os elementos da matriz resultante possam ser acessados.Em um pipeline em que o resultado deve ser processado posteriormente ou os resultados não cabem na memória como um todo, use:
-ExpandProperty
é explicada na resposta de Scott Saad .Para pequenas coleções de entradas (matrizes), você provavelmente não notará a diferença e, especialmente na linha de comando, às vezes é possível digitar o comando com facilidade.
Aqui está uma alternativa fácil de digitar , que, no entanto, é a abordagem mais lenta ; usa sintaxe simplificada
ForEach-Object
chamada instrução de operação (novamente, PSv3 +):; por exemplo, é fácil anexar a seguinte solução PSv3 + a um comando existente:Por uma questão de completude: O pouco conhecido método de array PSv4 +
.ForEach()
, mais abrangente discutido neste artigo , é outra alternativa :Essa abordagem é semelhante à enumeração de membros , com as mesmas vantagens, exceto que a lógica do pipeline não é aplicada; é marginalmente mais lento , embora ainda visivelmente mais rápido que o oleoduto.
Para extrair um único valor de propriedade por nome ( argumento de seqüência de caracteres ), esta solução está em pé de igualdade com a enumeração de membros (embora a última seja sintaticamente mais simples).
A variante de bloco de script , permite transformações arbitrárias ; é uma alternativa mais rápida - tudo na memória de uma só vez - ao
ForEach-Object
cmdlet baseado em pipeline (%
) .Comparando o desempenho das várias abordagens
Aqui estão exemplos de tempos para as várias abordagens, com base em uma coleção de
10,000
objetos de entrada , com média de 10 execuções; os números absolutos não são importantes e variam com base em muitos fatores, mas devem fornecer uma sensação de desempenho relativo (os tempos vêm de uma VM do Windows 10 de núcleo único:Importante
O desempenho relativo varia de acordo com se os objetos de entrada são instâncias de tipos .NET regulares (por exemplo, como saída por
Get-ChildItem
) ou[pscustomobject]
instâncias (por exemplo, como saída porConvert-FromCsv
).O motivo é que as
[pscustomobject]
propriedades são gerenciadas dinamicamente pelo PowerShell e podem acessá-las mais rapidamente do que as propriedades regulares de um tipo .NET regular (definido estaticamente). Ambos os cenários são abordados abaixo.Os testes usam coleções já com memória na íntegra como entrada, para focar no desempenho de extração de propriedade pura. Com um cmdlet de streaming / chamada de função como entrada, as diferenças de desempenho geralmente serão muito menos pronunciadas, pois o tempo gasto dentro dessa chamada pode ser responsável pela maior parte do tempo gasto.
Por questões de brevidade, o alias
%
é usado para oForEach-Object
cmdlet.Conclusões gerais , aplicáveis ao tipo e
[pscustomobject]
entrada regulares do .NET :A enumeração de membros (
$collection.Name
) e asforeach ($obj in $collection)
soluções são de longe as mais rápidas , por um fator 10 ou mais rápido que a solução mais rápida baseada em pipeline.Surpreendentemente, tem um
% Name
desempenho muito pior do que% { $_.Name }
- veja este problema do GitHub .O PowerShell Core supera consistentemente o Windows Powershell aqui.
Horários com tipos .NET regulares :
Conclusões:
.ForEach('Name')
supera claramente o desempenho.ForEach({ $_.Name })
. Curiosamente, no Windows PowerShell, o último é mais rápido, embora apenas marginalmente.Tempos com
[pscustomobject]
instâncias :Conclusões:
Observe como a
[pscustomobject]
entrada.ForEach('Name')
supera de longe a variante baseada em bloco de script.ForEach({ $_.Name })
,.Da mesma forma, a
[pscustomobject]
entrada torna o pipelineSelect-Object -ExpandProperty Name
mais rápido, no Windows PowerShell praticamente igual.ForEach({ $_.Name })
, mas no PowerShell Core ainda é 50% mais lento.Resumindo: com a exceção estranha de
% Name
, com[pscustomobject]
os métodos baseados em string de referência, as propriedades superam as baseadas em scriptblock.Código fonte dos testes :
Nota:
Baixar função
Time-Command
de esta Síntese para executar esses testes.Configure
$useCustomObjectInput
para$true
medir com[pscustomobject]
instâncias.fonte
Cuidado, a enumeração de membros funcionará apenas se a coleção em si não tiver nenhum membro com o mesmo nome. Portanto, se você tivesse uma matriz de objetos FileInfo, não poderia obter uma matriz de tamanhos de arquivo usando
E antes de dizer "bem, obviamente", considere isso. Se você tivesse uma matriz de objetos com uma propriedade de capacidade,
funcionaria bem, a menos que $ objarr não fosse realmente um [Array], mas, por exemplo, um [ArrayList]. Portanto, antes de usar a enumeração de membros, talvez seja necessário procurar dentro da caixa preta que contém sua coleção.
(Nota aos moderadores: este deve ser um comentário à resposta de rageandqq, mas ainda não tenho reputação suficiente.)
fonte
.ForEach()
método de matriz da seguinte maneira:$files.ForEach('Length')