Angenommen, wir haben ein Array von Objekten $ Objekte. Angenommen, diese Objekte haben die Eigenschaft "Name".
Das möchte ich tun
$results = @()
$objects | %{ $results += $_.Name }
Das funktioniert, aber kann es besser gemacht werden?
Wenn ich so etwas mache wie:
$results = objects | select Name
$results
ist ein Array von Objekten mit einer Name-Eigenschaft. Ich möchte, dass $ results ein Array von Namen enthält.
Gibt es einen besseren Weg?
arrays
powershell
member-enumeration
Sylvain Reverdy
quelle
quelle
$results = @($objects | %{ $_.Name })
. Dies kann manchmal bequemer über die Befehlszeile eingegeben werden, obwohl ich denke, dass Scotts Antwort im Allgemeinen besser ist.$objects | % Name
Antworten:
Ich denke, Sie können möglicherweise den
ExpandProperty
Parameter von verwendenSelect-Object
.Um beispielsweise die Liste des aktuellen Verzeichnisses abzurufen und nur die Eigenschaft Name anzuzeigen, gehen Sie wie folgt vor:
Dies gibt weiterhin DirectoryInfo- oder FileInfo-Objekte zurück. Sie können den Typ, der durch die Pipeline kommt, jederzeit überprüfen, indem Sie ihn an Get-Member (Alias
gm
) weiterleiten .Um das Objekt so zu erweitern, dass es dem Objekttyp entspricht, den Sie betrachten, können Sie Folgendes tun:
In Ihrem Fall können Sie einfach Folgendes tun, damit eine Variable ein Array von Zeichenfolgen ist, wobei die Zeichenfolgen die Eigenschaft Name sind:
quelle
Als noch einfachere Lösung können Sie einfach Folgendes verwenden:
Welches sollte
$results
mit einem Array aller 'Name' Eigenschaftswerte der Elemente in füllen$objects
.quelle
Exchange Management Shell
. Wenn wir Exchange verwenden, müssen wir$objects | select -Property Propname, OtherPropname
Ergänzend zu den bereits vorhandenen, hilfreichen Antworten mit Anleitungen, wann welcher Ansatz zu verwenden ist, und einem Leistungsvergleich .
DraußenVerwenden Sie einer Pipeline (PSv3 +):
wie in der Antwort von rageandqq gezeigt , die sowohl syntaktisch einfacher als auch syntaktisch einfacher ist viel schneller ist .foreach
Anweisung verwenden , deren Ausgabe Sie auch direkt einer Variablen zuweisen können:(Get-ChildItem).Name
), muss dieser Befehl zuerst vollständig ausgeführt werden, bevor auf die Elemente des resultierenden Arrays zugegriffen werden kann.Verwenden Sie in einer Pipeline, in der das Ergebnis weiterverarbeitet werden muss oder die Ergebnisse nicht in den gesamten Speicher passen, Folgendes:
-ExpandProperty
wird in der Antwort von Scott Saad erläutert .Bei kleinen Eingabesammlungen (Arrays) werden Sie den Unterschied wahrscheinlich nicht bemerken , und insbesondere in der Befehlszeile ist es manchmal wichtiger, den Befehl einfach eingeben zu können.
Hier ist eine einfach zu tippende Alternative , die jedoch der langsamste Ansatz ist . Es verwendet eine vereinfachte
ForEach-Object
Syntax, die als Operationsanweisung bezeichnet wird (wieder PSv3 +) :; Die folgende PSv3 + -Lösung lässt sich einfach an einen vorhandenen Befehl anhängen:Der Vollständigkeit halber: Die wenig bekannte PSv4 + -Array-
.ForEach()
Methode , die in diesem Artikel ausführlicher behandelt wird , ist eine weitere Alternative :Dieser Ansatz ähnelt der Mitgliederaufzählung mit denselben Kompromissen, außer dass die Pipeline-Logik nicht angewendet wird. es ist geringfügig langsamer , aber immer noch merklich schneller als die Pipeline.
Zum Extrahieren eines einzelnen Eigenschaftswerts nach Namen ( Zeichenfolgenargument ) entspricht diese Lösung der Elementaufzählung (obwohl letztere syntaktisch einfacher ist).
Die Skript-Block - Variante ermöglicht beliebige Transformationen ; Es ist eine schnellere Alternative zum Pipeline-basierten
ForEach-Object
Cmdlet (%
) .Vergleich der Leistung der verschiedenen Ansätze
Hier finden Sie Beispiel-Timings für die verschiedenen Ansätze, basierend auf einer Eingabesammlung von
10,000
Objekten , gemittelt über 10 Läufe. Die absoluten Zahlen sind nicht wichtig und variieren aufgrund vieler Faktoren. Sie sollten jedoch einen Eindruck von der relativen Leistung vermitteln (die Zeitangaben stammen von einer Single-Core-Windows 10-VM:Wichtig
Die relative Leistung hängt davon ab, ob es sich bei den Eingabeobjekten um Instanzen regulärer .NET-Typen (z. B. als Ausgabe von
Get-ChildItem
) oder um[pscustomobject]
Instanzen (z. B. als Ausgabe vonConvert-FromCsv
) handelt.Der Grund dafür ist, dass
[pscustomobject]
Eigenschaften von PowerShell dynamisch verwaltet werden und schneller auf sie zugreifen können als die regulären Eigenschaften eines (statisch definierten) regulären .NET-Typs. Beide Szenarien werden im Folgenden behandelt.Bei den Tests werden bereits im Speicher befindliche Sammlungen als Eingabe verwendet, um sich auf die reine Eigenschaftsextraktionsleistung zu konzentrieren. Mit einem Streaming-Cmdlet / Funktionsaufruf als Eingabe sind Leistungsunterschiede im Allgemeinen viel weniger ausgeprägt, da die in diesem Aufruf verbrachte Zeit den größten Teil der aufgewendeten Zeit ausmachen kann.
Der Kürze halber
%
wird für dasForEach-Object
Cmdlet ein Alias verwendet .Allgemeine Schlussfolgerungen , die sowohl für den regulären .NET-Typ als auch für die
[pscustomobject]
Eingabe gelten:Die Mitgliederaufzählung (
$collection.Name
) und dieforeach ($obj in $collection)
Lösungen sind bei weitem die schnellsten , um den Faktor 10 oder mehr schneller als die schnellste Pipeline-basierte Lösung.Überraschenderweise ist
% Name
die Leistung viel schlechter als% { $_.Name }
- siehe dieses GitHub-Problem .PowerShell Core übertrifft hier Windows Powershell durchweg.
Timings mit regulären .NET-Typen :
Schlussfolgerungen:
.ForEach('Name')
deutlich überlegen.ForEach({ $_.Name })
. In Windows PowerShell ist letzteres seltsamerweise schneller, wenn auch nur am Rande.Timings mit
[pscustomobject]
Instanzen :Schlussfolgerungen:
Beachten Sie, wie die
[pscustomobject]
Eingabe die.ForEach('Name')
auf Skriptblöcken basierende Variante bei weitem übertrifft.ForEach({ $_.Name })
.In ähnlicher Weise beschleunigt die
[pscustomobject]
Eingabe die Pipeline-BasisSelect-Object -ExpandProperty Name
in Windows PowerShell praktisch auf dem Niveau von.ForEach({ $_.Name })
, aber in PowerShell Core immer noch etwa 50% langsamer.Kurz gesagt: Mit der ungeraden Ausnahme
% Name
, mit[pscustomobject]
den String-basierten Methoden zur Referenzierung der Eigenschaften übertreffen die Skript-basiert ist.Quellcode für die Tests :
Hinweis:
Download-Funktion
Time-Command
von diesem Gist , um diese Tests auszuführen.Stellen Sie
$useCustomObjectInput
zu$true
mit messen ,[pscustomobject]
anstatt Instanzen.quelle
Achtung, die Mitgliederaufzählung funktioniert nur, wenn die Sammlung selbst kein gleichnamiges Mitglied hat. Wenn Sie also ein Array von FileInfo-Objekten hatten, konnten Sie mit nicht ein Array von Dateilängen abrufen
Und bevor Sie "gut offensichtlich" sagen, denken Sie darüber nach. Wenn Sie ein Array von Objekten mit einer Kapazitätseigenschaft hatten, dann
würde gut funktionieren, WENN $ objarr eigentlich kein [Array], sondern zum Beispiel eine [ArrayList] wäre. Also vor der Verwendung der Mitgliederaufzählung Sie möglicherweise in die Blackbox schauen, die Ihre Sammlung enthält.
(Hinweis für Moderatoren: Dies sollte ein Kommentar zur Antwort von rageandqq sein, aber ich habe noch nicht genug Ruf.)
quelle
.ForEach()
Array-Methode wie folgt zu verwenden:$files.ForEach('Length')