Powiedzmy, że mamy tablicę obiektów $ objects. Powiedzmy, że te obiekty mają właściwość „Nazwa”.
To jest to, co chcę zrobić
$results = @()
$objects | %{ $results += $_.Name }
To działa, ale czy można to zrobić w lepszy sposób?
Jeśli zrobię coś takiego:
$results = objects | select Name
$results
jest tablicą obiektów posiadających właściwość Name. Chcę, aby $ results zawierała tablicę nazw.
Czy jest lepszy sposób?
arrays
powershell
member-enumeration
Sylvain Reverdy
źródło
źródło
$results = @($objects | %{ $_.Name })
. Czasami wygodniej jest pisać w wierszu poleceń, chociaż myślę, że odpowiedź Scotta jest generalnie lepsza.$objects | % Name
Odpowiedzi:
Myślę, że możesz użyć
ExpandProperty
parametruSelect-Object
.Na przykład, aby uzyskać listę bieżącego katalogu i po prostu wyświetlić właściwość Name, należy wykonać następujące czynności:
Nadal zwraca obiekty DirectoryInfo lub FileInfo. Zawsze możesz sprawdzić typ przechodzący przez potok, przesyłając potok do Get-Member (alias
gm
).Aby więc rozwinąć obiekt tak, aby odpowiadał typowi właściwości, na którą patrzysz, możesz wykonać następujące czynności:
W twoim przypadku możesz po prostu wykonać następujące czynności, aby zmienna była tablicą ciągów, gdzie ciągi są właściwością Nazwa:
$objects = ls | select -ExpandProperty Name
źródło
Jeszcze łatwiejszym rozwiązaniem jest użycie:
$results = $objects.Name
Który powinien wypełnić
$results
tablicę wszystkich wartości właściwości „Nazwa” elementów w$objects
.źródło
Exchange Management Shell
. Korzystając z Exchange, musimy użyć$objects | select -Property Propname, OtherPropname
Uzupełnienie istniejących, pomocnych odpowiedzi wraz ze wskazówkami, kiedy zastosować które podejście, oraz porównaniem wyników .
Poza rurociągiem użyj (PSv3 +):
jak pokazano w odpowiedzi rageandqq , która jest zarówno prostsza składniowo, jak i znacznie szybsza .foreach
instrukcji , której wyjście możesz również przypisać bezpośrednio do zmiennej:(Get-ChildItem).Name
), To polecenie musi najpierw zostać wykonane do końca, zanim będzie można uzyskać dostęp do elementów wynikowej tablicy.W potoku, w którym wynik musi być dalej przetwarzany lub wyniki nie mieszczą się w całości w pamięci, użyj:
-ExpandProperty
jest wyjaśniona w odpowiedzi Scotta Saada .W przypadku małych kolekcji danych wejściowych (tablic) prawdopodobnie nie zauważysz różnicy , a zwłaszcza w wierszu poleceń, czasami łatwiejsze wpisanie polecenia jest ważniejsze.
Oto łatwa do wpisania alternatywa , która jest jednak najwolniejszym podejściem ; używa uproszczonej
ForEach-Object
składni zwanej instrukcją operacji (znowu PSv3 +):; np. następujące rozwiązanie PSv3 + można łatwo dodać do istniejącego polecenia:$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Gwoli ścisłości
.ForEach()
: Inną alternatywą jest mało znana metoda tablicowa PSv4 + , bardziej wszechstronna omówiona w tym artykule :# By property name (string): $objects.ForEach('Name') # By script block (more flexibility; like ForEach-Object) $objects.ForEach({ $_.Name })
To podejście jest podobne do wyliczania elementów członkowskich , z tymi samymi kompromisami, z wyjątkiem tego, że logika potoków nie jest stosowana; jest nieznacznie wolniejszy , choć nadal zauważalnie szybszy niż rurociąg.
W przypadku wyodrębniania pojedynczej wartości właściwości według nazwy ( argument ciągu ) to rozwiązanie jest równe wyliczaniu elementów członkowskich (chociaż to drugie jest prostsze składniowo).
Skrypt-block wariant umożliwia arbitralne transformacji ; jest to szybsza alternatywa dla polecenia
ForEach-Object
cmdlet (%
) opartego na potokach .Porównanie wydajności różnych podejść
Oto przykładowe czasy dla różnych podejść, oparte na wejściowej kolekcji
10,000
obiektów , uśrednione z 10 przebiegów; liczby bezwzględne nie są ważne i różnią się w zależności od wielu czynników, ale powinny dać poczucie względnej wydajności (czasy pochodzą z jednordzeniowej maszyny wirtualnej z systemem Windows 10:Ważny
Względna wydajność różni się w zależności od tego, czy obiekty wejściowe są instancjami zwykłych typów .NET (np. Jako dane wyjściowe przez
Get-ChildItem
), czy[pscustomobject]
instancjami (np. Jako dane wyjściowe przezConvert-FromCsv
).Powodem jest to, że
[pscustomobject]
właściwości są dynamicznie zarządzane przez PowerShell i może uzyskać do nich dostęp szybciej niż zwykłe właściwości (zdefiniowanego statycznie) zwykłego typu .NET. Oba scenariusze omówiono poniżej.Testy używają kolekcji już znajdujących się w pamięci jako danych wejściowych, aby skupić się na wydajności wyodrębniania czystej właściwości. W przypadku przesyłania strumieniowego polecenia cmdlet / wywołania funkcji jako danych wejściowych różnice w wydajności będą na ogół znacznie mniej wyraźne, ponieważ czas spędzony wewnątrz tego wywołania może stanowić większość spędzonego czasu.
Dla zwięzłości alias
%
jest używany w poleceniuForEach-Object
cmdlet.Ogólne wnioski , dotyczące zarówno zwykłego typu .NET, jak i
[pscustomobject]
danych wejściowych:Wyliczanie elementów członkowskich (
$collection.Name
) iforeach ($obj in $collection)
rozwiązania są zdecydowanie najszybsze , o współczynnik 10 lub więcej szybciej niż najszybsze rozwiązanie oparte na potokach.O dziwo,
% Name
działa znacznie gorzej niż% { $_.Name }
- zobacz ten numer GitHub .PowerShell Core konsekwentnie przewyższa tutaj Windows Powershell.
Czasy ze zwykłymi typami .NET :
Wnioski:
.ForEach('Name')
wyraźnie przewyższa.ForEach({ $_.Name })
. Co ciekawe, w programie Windows PowerShell ten drugi jest szybszy, choć tylko nieznacznie.Czasy z
[pscustomobject]
instancjami :Wnioski:
Uwaga jak z
[pscustomobject]
wejściem.ForEach('Name')
zdecydowanie przewyższa skrypt blok oparty wariantu.ForEach({ $_.Name })
.Podobnie
[pscustomobject]
dane wejściowe sprawiają , że proces oparty na potoku jestSelect-Object -ExpandProperty Name
szybszy, w programie Windows PowerShell praktycznie na równi z.ForEach({ $_.Name })
, ale w programie PowerShell Core nadal o około 50% wolniejszy.W skrócie: z dziwnym wyjątkiem
% Name
,[pscustomobject]
metody odwoływania się do właściwości oparte na ciągach znaków przewyższają te oparte na skryptach.Kod źródłowy do testów :
Uwaga:
Pobierz funkcję
Time-Command
z tego streszczenia, aby uruchomić te testy.Zamiast tego ustaw
$useCustomObjectInput
na$true
pomiar z[pscustomobject]
instancjami.$count = 1e4 # max. input object count == 10,000 $runs = 10 # number of runs to average # Note: Using [pscustomobject] instances rather than instances of # regular .NET types changes the performance characteristics. # Set this to $true to test with [pscustomobject] instances below. $useCustomObjectInput = $false # Create sample input objects. if ($useCustomObjectInput) { # Use [pscustomobject] instances. $objects = 1..$count | % { [pscustomobject] @{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } } } else { # Use instances of a regular .NET type. # Note: The actual count of files and folders in your home dir. tree # may be less than $count $objects = Get-ChildItem -Recurse $HOME | Select-Object -First $count } Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..." # An array of script blocks with the various approaches. $approaches = { $objects | Select-Object -ExpandProperty Name }, { $objects | % Name }, { $objects | % { $_.Name } }, { $objects.ForEach('Name') }, { $objects.ForEach({ $_.Name }) }, { $objects.Name }, { foreach($o in $objects) { $o.Name } } # Time the approaches and sort them by execution time (fastest first): Time-Command $approaches -Count $runs | Select Factor, Command, Secs*
źródło
Uwaga, wyliczanie elementów członkowskich działa tylko wtedy, gdy sama kolekcja nie ma elementu członkowskiego o tej samej nazwie. Więc jeśli masz tablicę obiektów FileInfo, nie możesz uzyskać tablicy długości plików przy użyciu
$files.length # evaluates to array length
Zanim powiesz „oczywiście”, zastanów się nad tym. Gdybyś miał wtedy tablicę obiektów z właściwością capacity
$objarr.capacity
działałoby dobrze, chyba że $ objarr faktycznie nie było [Array], ale na przykład [ArrayList]. Dlatego przed użyciem wyliczania członków może być konieczne zajrzenie do czarnego pola zawierającego Twoją kolekcję.
(Uwaga dla moderatorów: to powinien być komentarz do odpowiedzi rageandqq, ale nie mam jeszcze wystarczającej reputacji).
źródło
.ForEach()
metody tablicowej w następujący sposób:$files.ForEach('Length')