Disons que nous avons un tableau d'objets $ objets. Disons que ces objets ont une propriété "Name".
C'est ce que je veux faire
$results = @()
$objects | %{ $results += $_.Name }
Cela fonctionne, mais cela peut-il être fait d'une meilleure manière?
Si je fais quelque chose comme:
$results = objects | select Name
$results
est un tableau d'objets ayant une propriété Name. Je veux que $ results contienne un tableau de noms.
Y a-t-il un meilleur moyen?
arrays
powershell
member-enumeration
Sylvain Reverdy
la source
la source
$results = @($objects | %{ $_.Name })
. Cela peut parfois être plus pratique à taper sur la ligne de commande, même si je pense que la réponse de Scott est généralement meilleure.$objects | % Name
Réponses:
Je pense que vous pourrez peut-être utiliser le
ExpandProperty
paramètre deSelect-Object
.Par exemple, pour obtenir la liste du répertoire actuel et simplement afficher la propriété Name, on ferait ce qui suit:
Cela renvoie toujours des objets DirectoryInfo ou FileInfo. Vous pouvez toujours inspecter le type passant par le pipeline en redirigeant vers Get-Member (alias
gm
).Ainsi, pour développer l'objet afin qu'il soit celui du type de propriété que vous regardez, vous pouvez effectuer les opérations suivantes:
Dans votre cas, vous pouvez simplement faire ce qui suit pour qu'une variable soit un tableau de chaînes, où les chaînes sont la propriété Name:
la source
Comme solution encore plus simple, vous pouvez simplement utiliser:
Qui devrait remplir
$results
avec un tableau de toutes les valeurs de propriété 'Name' des éléments dans$objects
.la source
Exchange Management Shell
. Lors de l'utilisation d'Exchange, nous devons utiliser$objects | select -Property Propname, OtherPropname
Pour compléter les réponses préexistantes et utiles avec des conseils sur le moment d'utiliser quelle approche et une comparaison des performances .
En dehors d'un pipeline, utilisez (PSv3 +):
comme démontré dans la réponse de rageandqq , qui est à la fois syntaxiquement plus simple et beaucoup plus rapide .foreach
instruction , dont vous pouvez également affecter la sortie directement à une variable:(Get-ChildItem).Name
), cette commande doit d'abord s'exécuter jusqu'à la fin avant que les éléments du tableau résultant ne soient accessibles.Dans un pipeline où le résultat doit être traité plus avant ou où les résultats ne rentrent pas dans la mémoire dans son ensemble, utilisez:
-ExpandProperty
est expliqué dans la réponse de Scott Saad .Pour les petites collections d'entrée (tableaux), vous ne remarquerez probablement pas la différence et, en particulier sur la ligne de commande, il est parfois plus important de pouvoir taper la commande facilement.
Voici une alternative facile à taper , qui est cependant l' approche la plus lente ; il utilise une
ForEach-Object
syntaxe simplifiée appelée instruction d'opération (encore une fois, PSv3 +):; Par exemple, la solution PSv3 + suivante est facile à ajouter à une commande existante:Par souci d'exhaustivité: la méthode de tableau PSv4 +
.ForEach()
peu connue , plus complète discutée dans cet article , est encore une autre alternative :Cette approche est similaire à l'énumération des membres , avec les mêmes compromis, sauf que la logique de pipeline n'est pas appliquée; il est légèrement plus lent , mais toujours sensiblement plus rapide que le pipeline.
Pour extraire une seule valeur de propriété par nom ( argument de chaîne ), cette solution est comparable à l'énumération des membres (bien que cette dernière soit syntaxiquement plus simple).
La variante de bloc de script permet des transformations arbitraires ; c'est une alternative plus rapide - tout en mémoire à la fois - à la
ForEach-Object
cmdlet basée sur un pipeline (%
) .Comparer les performances des différentes approches
Voici des exemples de minutages pour les différentes approches, basés sur une collection d'
10,000
objets d' entrée , moyennés sur 10 exécutions; les nombres absolus ne sont pas importants et varient en fonction de nombreux facteurs, mais cela devrait vous donner une idée des performances relatives (les horaires proviennent d'une machine virtuelle Windows 10 à un seul cœur:Important
Les performances relatives varient selon que les objets d'entrée sont des instances de types .NET normaux (par exemple, en sortie par
Get-ChildItem
) ou des[pscustomobject]
instances (par exemple, en sortie parConvert-FromCsv
).La raison est que les
[pscustomobject]
propriétés sont gérées dynamiquement par PowerShell et qu'il peut y accéder plus rapidement que les propriétés normales d'un type .NET régulier (défini de manière statique). Les deux scénarios sont traités ci-dessous.Les tests utilisent des collections déjà en mémoire dans leur intégralité comme entrée, afin de se concentrer sur les performances d'extraction de propriété pure. Avec un appel d'applet de commande / de fonction en continu comme entrée, les différences de performances seront généralement beaucoup moins prononcées, car le temps passé à l'intérieur de cet appel peut représenter la majorité du temps passé.
Par souci de concision, l'alias
%
est utilisé pour l'ForEach-Object
applet de commande.Conclusions générales , applicables à la fois au type et à l'
[pscustomobject]
entrée .NET réguliers :L'énumération des membres (
$collection.Name
) et lesforeach ($obj in $collection)
solutions sont de loin les plus rapides , d'un facteur 10 ou plus que la solution basée sur le pipeline la plus rapide.Étonnamment, les
% Name
performances sont bien pires que% { $_.Name }
- voyez ce problème GitHub .PowerShell Core surpasse constamment Windows Powershell ici.
Timings avec les types .NET normaux :
Conclusions:
.ForEach('Name')
surpasse clairement.ForEach({ $_.Name })
. Dans Windows PowerShell, curieusement, ce dernier est plus rapide, bien que marginalement.Timings avec
[pscustomobject]
instances :Conclusions:
Notez comment avec
[pscustomobject]
entrée.ForEach('Name')
par la variante surpasse largement basée sur un script bloc,.ForEach({ $_.Name })
.De même, l'
[pscustomobject]
entrée rend le pipelineSelect-Object -ExpandProperty Name
plus rapide, dans Windows PowerShell pratiquement au même niveau que.ForEach({ $_.Name })
, mais dans PowerShell Core encore environ 50% plus lent.En bref: à l'exception étrange de
% Name
, avec[pscustomobject]
les méthodes de référencement basées sur des chaînes, les propriétés surpassent celles basées sur des blocs de script.Code source pour les tests :
Remarque:
Téléchargez la fonction à
Time-Command
partir de ce Gist pour exécuter ces tests.Définissez plutôt
$useCustomObjectInput
sur$true
pour mesurer avec des[pscustomobject]
instances.la source
Attention, l' énumération des membres ne fonctionne que si la collection elle-même n'a aucun membre du même nom. Donc, si vous aviez un tableau d'objets FileInfo, vous ne pouviez pas obtenir un tableau de longueurs de fichier en utilisant
Et avant de dire «bien évidemment», considérez ceci. Si vous aviez un tableau d'objets avec une propriété de capacité, alors
fonctionnerait bien A MOINS QUE $ objarr ne soit en fait pas un [Array] mais, par exemple, un [ArrayList]. Donc, avant d'utiliser l' énumération des membres, vous devrez peut-être regarder à l'intérieur de la boîte noire contenant votre collection.
(Note aux modérateurs: cela devrait être un commentaire sur la réponse de rageandqq mais je n'ai pas encore assez de réputation.)
la source
.ForEach()
méthode de tableau comme suit:$files.ForEach('Length')