Digamos que tenemos una matriz de objetos $ objetos. Digamos que estos objetos tienen una propiedad "Nombre".
Esto es lo que quiero hacer
$results = @()
$objects | %{ $results += $_.Name }
Esto funciona, pero ¿se puede hacer de una mejor manera?
Si hago algo como:
$results = objects | select Name
$results
es una matriz de objetos que tiene una propiedad Name. Quiero que $ results contenga una variedad de nombres.
¿Hay una mejor manera?
arrays
powershell
member-enumeration
Sylvain Reverdy
fuente
fuente
$results = @($objects | %{ $_.Name })
. Esto puede ser más conveniente para escribir en la línea de comando a veces, aunque creo que la respuesta de Scott es generalmente mejor.$objects | % Name
Respuestas:
Creo que podrías usar el
ExpandProperty
parámetro deSelect-Object
.Por ejemplo, para obtener la lista del directorio actual y solo mostrar la propiedad Name, se haría lo siguiente:
Esto sigue devolviendo objetos DirectoryInfo o FileInfo. Siempre puede inspeccionar el tipo que llega a través de la canalización canalizando a Get-Member (alias
gm
).Por lo tanto, para expandir el objeto para que sea el tipo de propiedad que está viendo, puede hacer lo siguiente:
En su caso, puede hacer lo siguiente para que una variable sea una matriz de cadenas, donde las cadenas son la propiedad Nombre:
fuente
Como una solución aún más fácil, puede usar:
Que debería llenarse
$results
con una matriz de todos los valores de propiedad 'Nombre' de los elementos en$objects
.fuente
Exchange Management Shell
. Cuando usemos Exchange necesitamos usarlo$objects | select -Property Propname, OtherPropname
Para complementar las respuestas útiles y preexistentes con orientación sobre cuándo usar qué enfoque y una comparación de rendimiento .
Fuera de una tubería, use (PSv3 +):
como se demostró en la respuesta de rageandqq , que es sintácticamente más simple y mucho más rápido .foreach
declaración , cuya salida también puede asignar directamente a una variable:(Get-ChildItem).Name
), ese comando primero debe ejecutarse hasta su finalización antes de que se pueda acceder a los elementos de la matriz resultante.En una tubería donde el resultado debe procesarse más o los resultados no caben en la memoria como un todo, use:
-ExpandProperty
se explica en la respuesta de Scott Saad .Para pequeñas colecciones de entrada (matrices), probablemente no notará la diferencia y, especialmente en la línea de comandos, a veces es más importante poder escribir el comando fácilmente.
Aquí hay una alternativa fácil de escribir , que, sin embargo, es el enfoque más lento ; utiliza una
ForEach-Object
sintaxis simplificada llamada declaración de operación (nuevamente, PSv3 +):; por ejemplo, la siguiente solución PSv3 + es fácil de agregar a un comando existente:En aras de la exhaustividad: el método de matriz PSv4 +
.ForEach()
poco conocido , más completo que se analiza en este artículo , es otra alternativa :Este enfoque es similar a la enumeración de miembros , con las mismas compensaciones, excepto que no se aplica la lógica de canalización ; es marginalmente más lento , aunque todavía notablemente más rápido que la tubería.
Para extraer un solo valor de propiedad por nombre ( argumento de cadena ), esta solución está a la par con la enumeración de miembros (aunque esta última es sintácticamente más simple).
La variante de bloque de script permite transformaciones arbitrarias ; es una alternativa más rápida, todo en memoria a la vez, al
ForEach-Object
cmdlet basado en canalización (%
) .Comparar el rendimiento de los distintos enfoques.
Aquí hay ejemplos de tiempos para los diversos enfoques, basados en una colección de entrada de
10,000
objetos , promediados en 10 ejecuciones; los números absolutos no son importantes y varían en función de muchos factores, pero deberían darle una sensación de rendimiento relativo (los tiempos provienen de una VM de Windows 10 de un solo núcleo:Importante
El rendimiento relativo varía en función de si los objetos de entrada son instancias de tipos .NET regulares (por ejemplo, como resultado
Get-ChildItem
) o[pscustomobject]
instancias (por ejemplo, como resultadoConvert-FromCsv
).La razón es que
[pscustomobject]
PowerShell administra dinámicamente las propiedades y puede acceder a ellas más rápidamente que las propiedades regulares de un tipo .NET regular (definido estáticamente). Ambos escenarios se cubren a continuación.Las pruebas utilizan colecciones ya en memoria llena como entrada, para enfocarse en el rendimiento de extracción de propiedad pura. Con una llamada de cmdlet / función de transmisión como entrada, las diferencias de rendimiento generalmente serán mucho menos pronunciadas, ya que el tiempo pasado dentro de esa llamada puede representar la mayor parte del tiempo dedicado.
Para abreviar,
%
se usa alias para elForEach-Object
cmdlet.Conclusiones generales , aplicables tanto al tipo regular de .NET como a la
[pscustomobject]
entrada:La enumeración de miembros (
$collection.Name
) y lasforeach ($obj in $collection)
soluciones son, con mucho, las más rápidas , por un factor de 10 o más más rápido que la solución basada en canalizaciones más rápida.Sorprendentemente,
% Name
funciona mucho peor que% { $_.Name }
: vea este problema de GitHub .PowerShell Core supera constantemente a Windows Powershell aquí.
Tiempos con tipos regulares de .NET :
Conclusiones:
.ForEach('Name')
claramente supera el rendimiento.ForEach({ $_.Name })
. En Windows PowerShell, curiosamente, este último es más rápido, aunque solo marginalmente.Tiempos con
[pscustomobject]
instancias :Conclusiones:
Nota cómo con
[pscustomobject]
entrada.ForEach('Name')
por Supera con mucho, el script del bloque variante basa,.ForEach({ $_.Name })
.Del mismo modo, la
[pscustomobject]
entrada hace que la canalización seaSelect-Object -ExpandProperty Name
más rápida, en Windows PowerShell prácticamente a la par.ForEach({ $_.Name })
, pero en PowerShell Core todavía un 50% más lento.En resumen: con la extraña excepción de
% Name
, con[pscustomobject]
los métodos basados en cadenas para hacer referencia a las propiedades superan a las basadas en scriptblock.Código fuente para las pruebas :
Nota:
Descargue la función
Time-Command
de este Gist para ejecutar estas pruebas.Establezca
$useCustomObjectInput
a$true
medida con[pscustomobject]
instancias en su lugar.fuente
Precaución, la enumeración de miembros solo funciona si la colección en sí no tiene ningún miembro con el mismo nombre. Entonces, si tuviera una matriz de objetos FileInfo, no podría obtener una matriz de longitudes de archivo utilizando
Y antes de decir "bueno obviamente", considere esto. Si tenía una matriz de objetos con una propiedad de capacidad, entonces
funcionaría bien A MENOS QUE $ objarr en realidad no fuera una [Array] sino, por ejemplo, una [ArrayList]. Entonces, antes de usar la enumeración de miembros, es posible que deba mirar dentro del cuadro negro que contiene su colección.
(Nota para los moderadores: este debería ser un comentario sobre la respuesta de rageandqq pero todavía no tengo suficiente reputación).
fuente
.ForEach()
método de matriz de la siguiente manera:$files.ForEach('Length')