Katakanlah kita memiliki array objek $ objek. Katakanlah benda-benda ini memiliki properti "Nama".
Ini yang ingin saya lakukan
$results = @()
$objects | %{ $results += $_.Name }
Ini bekerja, tetapi bisakah itu dilakukan dengan cara yang lebih baik?
Jika saya melakukan sesuatu seperti:
$results = objects | select Name
$results
adalah larik objek yang memiliki properti Nama. Saya ingin $ hasil mengandung array Nama.
Apakah ada cara yang lebih baik?
arrays
powershell
member-enumeration
Sylvain Reverdy
sumber
sumber
$results = @($objects | %{ $_.Name })
. Ini bisa lebih mudah untuk mengetik di baris perintah di kali, meskipun saya pikir jawaban Scott umumnya lebih baik.$objects | % Name
Jawaban:
Saya pikir Anda mungkin dapat menggunakan
ExpandProperty
parameterSelect-Object
.Sebagai contoh, untuk mendapatkan daftar direktori saat ini dan hanya memiliki properti Nama yang ditampilkan, orang akan melakukan hal berikut:
Ini masih mengembalikan objek DirectoryInfo atau FileInfo. Anda selalu dapat memeriksa jenis yang datang melalui pipa dengan memipet ke Get-Member (alias
gm
).Jadi, untuk memperluas objek menjadi tipe properti yang Anda lihat, Anda dapat melakukan hal berikut:
Dalam kasus Anda, Anda bisa melakukan yang berikut untuk membuat variabel menjadi array string, di mana string adalah properti Name:
sumber
Sebagai solusi yang lebih mudah, Anda bisa menggunakan:
Yang harus diisi
$results
dengan larik semua nilai properti 'Nama' elemen di$objects
.sumber
Exchange Management Shell
. Saat menggunakan Exchange, kita perlu menggunakan$objects | select -Property Propname, OtherPropname
Untuk melengkapi jawaban yang sudah ada sebelumnya, jawaban yang bermanfaat dengan panduan kapan harus menggunakan pendekatan mana dan perbandingan kinerja .
Di luar pipa, gunakan (PSv3 +):
seperti yang ditunjukkan dalam jawaban rageandqq , yang secara sintaksis lebih sederhana dan lebih cepat .foreach
pernyataan , yang outputnya dapat Anda tetapkan langsung ke variabel:(Get-ChildItem).Name
), perintah itu harus terlebih dahulu dijalankan sampai selesai sebelum elemen array yang dihasilkan dapat diakses.Dalam pipa yang hasilnya harus diproses lebih lanjut atau hasilnya tidak sesuai dengan memori secara keseluruhan, gunakan:
-ExpandProperty
dijelaskan dalam jawaban Scott Saad .Untuk koleksi input kecil (array), Anda mungkin tidak akan melihat perbedaannya , dan, terutama pada baris perintah, kadang-kadang bisa mengetik perintah dengan mudah lebih penting.
Berikut ini adalah alternatif yang mudah diketik , yang, bagaimanapun, merupakan pendekatan paling lambat ; menggunakan sintaks yang disederhanakan yang
ForEach-Object
disebut pernyataan operasi (sekali lagi, PSv3 +):; mis., solusi PSv3 + berikut ini mudah ditambahkan ke perintah yang ada:Demi kelengkapan: Metode array PSv4 + yang kurang
.ForEach()
dikenal , lebih lengkap dibahas dalam artikel ini , adalah alternatif lain :Pendekatan ini mirip dengan penghitungan anggota , dengan pengorbanan yang sama, kecuali bahwa logika pipa tidak diterapkan; ini sedikit lebih lambat , meskipun masih terasa lebih cepat daripada pipa.
Untuk mengekstraksi nilai properti tunggal dengan nama ( argumen string ), solusi ini setara dengan enumerasi anggota (meskipun yang terakhir secara sintaksis lebih sederhana).
The Script-blok varian , memungkinkan sewenang-wenang transformasi ; ini adalah alternatif yang lebih cepat - all-in-memory-at-sekaligus - ke
ForEach-Object
cmdlet berbasis-pipa (%
) .Membandingkan kinerja berbagai pendekatan
Berikut adalah contoh waktu untuk berbagai pendekatan, berdasarkan pada kumpulan input
10,000
objek , rata-rata di 10 run; angka absolut tidak penting dan bervariasi berdasarkan banyak faktor, tetapi seharusnya memberi Anda rasa kinerja relatif (timing berasal dari satu-core Windows 10 VM:Penting
Kinerja relatif bervariasi berdasarkan pada apakah objek input adalah instance dari .NET Type (misalnya, sebagai output oleh
Get-ChildItem
) atau[pscustomobject]
instance (misalnya, sebagai output olehConvert-FromCsv
).Alasannya adalah bahwa
[pscustomobject]
properti dikelola secara dinamis oleh PowerShell, dan itu dapat mengaksesnya lebih cepat daripada properti biasa dari tipe .NET biasa (yang ditentukan secara statis). Kedua skenario dibahas di bawah ini.Pengujian menggunakan koleksi yang sudah ada dalam memori-dalam-penuh sebagai input, sehingga dapat fokus pada kinerja ekstraksi properti murni. Dengan cmdlet streaming / panggilan fungsi sebagai input, perbedaan kinerja umumnya akan jauh lebih sedikit, karena waktu yang dihabiskan di dalam panggilan itu mungkin merupakan bagian dari sebagian besar waktu yang dihabiskan.
Untuk singkatnya, alias
%
digunakan untukForEach-Object
cmdlet.Kesimpulan umum , berlaku untuk tipe dan
[pscustomobject]
input .NET reguler :Penghitungan anggota (
$collection.Name
) danforeach ($obj in $collection)
solusi sejauh ini tercepat , dengan faktor 10 atau lebih cepat daripada solusi berbasis pipa tercepat.Anehnya,
% Name
performanya jauh lebih buruk daripada% { $_.Name }
- lihat masalah GitHub ini .PowerShell Core secara konsisten mengungguli Windows Powershell di sini.
Pengaturan waktu dengan tipe .NET biasa :
Kesimpulan:
.ForEach('Name')
jelas mengungguli.ForEach({ $_.Name })
. Di Windows PowerShell, anehnya, yang terakhir lebih cepat, meskipun hanya sedikit.Pengaturan waktu dengan
[pscustomobject]
instance :Kesimpulan:
Perhatikan bagaimana dengan
[pscustomobject]
input yang.ForEach('Name')
jauh melebihi varian berbasis blok script.ForEach({ $_.Name })
,.Demikian pula,
[pscustomobject]
input membuat berbasis pipaSelect-Object -ExpandProperty Name
lebih cepat, di Windows PowerShell hampir setara.ForEach({ $_.Name })
, tetapi di PowerShell Core masih sekitar 50% lebih lambat.Singkatnya: Dengan pengecualian aneh
% Name
, dengan[pscustomobject]
metode berbasis string untuk mereferensikan properti mengungguli yang berbasis scriptblock.Kode sumber untuk tes :
catatan:
Unduh fungsi
Time-Command
dari Intisari ini untuk menjalankan tes ini.Tetapkan
$useCustomObjectInput
untuk$true
mengukur dengan[pscustomobject]
instance sebagai gantinya.sumber
Perhatian, penghitungan anggota hanya berfungsi jika koleksi itu sendiri tidak memiliki anggota dengan nama yang sama. Jadi jika Anda memiliki larik objek FileInfo, Anda tidak bisa mendapatkan larik panjang file dengan menggunakan
Dan sebelum Anda mengatakan "jelas sekali", pertimbangkan ini. Jika Anda memiliki array objek dengan properti kapasitas, maka
akan berfungsi dengan baik KECUALI $ objarr sebenarnya bukan [Array] tetapi, misalnya, [ArrayList]. Jadi sebelum menggunakan enumerasi anggota Anda mungkin harus melihat ke dalam kotak hitam yang berisi koleksi Anda.
(Catatan untuk moderator: ini harus menjadi komentar pada jawaban rageandqq tetapi saya belum memiliki reputasi yang cukup.)
sumber
.ForEach()
metode array sebagai berikut:$files.ForEach('Length')