Selecione os valores de uma propriedade em todos os objetos de uma matriz em PowerShell

Digamos que temos um conjunto de objectos. Digamos que estes objectos têm uma propriedade de "nome".

Isto é o que eu quero fazer.
 $results = @()
 $objects | %{ $results += $_.Name }
Isto funciona, mas pode ser feito de uma forma melhor?

Se eu fizer algo como:

 $results = objects | select Name

$results é uma matriz de objetos com uma propriedade de nome. Quero que os resultados contenham uma série de nomes.

Há uma maneira melhor?

 92
Author: Peter Mortensen, 2011-03-03

3 answers

Eu acho que você pode ser capaz de usar o parâmetro ExpandProperty de Select-Object.

Por exemplo, para obter a lista da pasta actual e apenas ter a propriedade do nome apresentada, faria-se o seguinte:

ls | select -Property Name

Isto ainda está a devolver os objectos DirectoryInfo ou FileInfo. Você pode sempre inspecionar o tipo que vem através do oleoduto através de piping para Get-Member (Também conhecido por gm).

ls | select -Property Name | gm

Então, para expandir o objeto para ser o do tipo de propriedade que você é olhando, você pode fazer o seguinte:

ls | select -ExpandProperty Name

No seu caso, pode apenas fazer o seguinte para que uma variável seja um array de strings, onde as strings são a propriedade Nome:

$objects = ls | select -ExpandProperty Name
 161
Author: Scott Saad, 2016-02-08 12:17:07

Como uma solução ainda mais fácil, você poderia apenas usar:

$results = $objects.Name

Que deve preencher $results com uma matriz de todos os valores de propriedade 'Nome' dos elementos em $objects.

 50
Author: rageandqq, 2014-07-24 15:58:18

Para complementar as respostas pré-existentes e úteis com a orientação de quando usar qual abordageme uma comparação de desempenho .

  • Fora de um pipeline, use:

    $objects.Name
    (PSv3+), como demonstrado no rageandqq a resposta, que é sintaticamente mais simples e muito mais rápido.

    • aceder a uma propriedade no nível para obter os valores dos seus membros como um matriz {[34] } é chamada enumeração dos membros e é uma característica PSv3+ ;
    • em alternativa, emPSv2 , utilize o foreach instrução , cujo resultado Poderá também atribuir directamente a uma variável:
      $results = foreach ($obj in $objects) { $obj.Name }
    • Tradeoffs:
      • tanto o conjunto de dados de entrada como de saída deve caber na memória como um todo.
      • se a colecção de dados for ela própria o resultado de um comando (pipeline) (por exemplo, (Get-ChildItem).Name), que comando deve primeiro executar até completar antes que os elementos da matriz resultante possam ser acessados.
  • Num oleoduto quando o resultado deve ser processado ou os resultados não se encaixam na memória como um todo, use:

    $objects | Select-Object -ExpandProperty Name

      A necessidade de -ExpandPropertyé explicada na resposta de Scott Saad .
  • Recebes os benefícios habituais do processamento um a um., que normalmente produz saída imediatamente e mantém a memória usar constante (a menos que você finalmente coletar os resultados na memória de qualquer maneira).
  • Tradeoff:
    • a utilização do oleoduto é comparativamente lenta .

Parasmall input collections (arrays), você provavelmente não vai notar a diferença, e, especialmente na linha de comandos, às vezes ser capaz de digitar o comando facilmente é mais importante.


Aqui está uma alternativa fácil de digitar, que, no entanto, é a abordagem mais lenta ; utiliza sintaxe simplificada ForEach-Object chamada de declaração de operação (mais uma vez, PSv3+): ; por exemplo, a seguinte solução PSv3+ é fácil de adicionar a um comando existente:

$objects | % Name      # short for: $objects | ForEach-Object -Process { $_.Name }

Por uma questão de exaustividade: o pouco conhecido PSv4+ .ForEach() método de recolha é mais um alternativa:

# By property name (string):
$objects.ForEach('Name')

# By script block (much slower):
$objects.ForEach({ $_.Name })
  • Esta abordagem é semelhante à enumeração de membros , com os mesmos tradeoffs, exceto que a lógica do oleoduto é Não aplicada; émarginalmente mais lenta , embora ainda visivelmente mais rápida do que o oleoduto.

  • Para extrair um único valor de propriedade por nome (string argumento), esta solução está a par com a enumeração de membros (embora esta última seja sintaticamente simplificado).

  • O script-bloco variante, embora muito mais lento, permite arbitrário transformações; é mais rápido tudo - em-memória-em-uma - vez- alternativa para o pipeline de base ForEach-Object cmdlet.


Comparar o desempenho das várias abordagens

Aqui estão cronogramas das amostras para as várias abordagens, com base numa colecção de dados de entrada de 100,000 objectos , em média ao longo de 100 corridas; os números absolutos não são importantes e variam com base em muitos factores, mas deve dar-lhe uma sensação de relativa desempenho:

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
  • A solução do método de recolha baseado no nome da propriedade / enumeração de membros é mais rápida por um factor de 10+ do que a solução mais rápida baseada no oleoduto.

  • O foreach declaração a solução é cerca de 2.5 mais lenta, mas ainda cerca de 4-5 vezes mais rápida do que o gasoduto mais rápido solucao.

  • O uso de um bloco de script com a solução de método de recolha (.ForEach({ ... }) atrasa as coisas dramaticamente, de modo que está praticamente a par com a solução mais rápida baseada em pipeline (Select-Object -ExpandProperty).

  • % Number (ForEach-Object Number), curiosamente, tem pior desempenho, embora % Number seja o equivalente conceitual de % { $_.Number }).


Código-fonte dos ensaios:

Nota: Função de Download Time-Command de este Gist para fazer estes testes.

$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
 8
Author: mklement0, 2018-08-24 16:03:07