Выберите значения свойств для всех объектов массива в PowerShell
допустим, у нас есть массив объектов $objects. Допустим, эти объекты имеют свойство "имя".
Это то, что я хочу сделать
$results = @()
$objects | %{ $results += $_.Name }
это работает, но можно ли это сделать лучше?
Если я делаю что-то вроде:
$results = objects | select Name
$results
- это массив объектов, имеющих свойство name. Я хочу, чтобы $results содержал массив имен.
есть ли лучший способ?
3 ответа:
как еще более простое решение, вы можете просто использовать:
$results = $objects.Name
, которые необходимо заполнить
$results
с массивом всех значений свойств 'Name' элементов в$objects
.
чтобы дополнить уже существующие, полезные ответы с руководством когда использовать, какой подход и сравнение производительности.
за пределами трубопровода, использовать:
$objects.Name(PSv3+), как показано в ответ rageandqq, что синтаксически проще и быстрее.
- доступ к свойству в коллекциясравнительно медленно.
на маленький входные коллекции (массивы), вы, вероятно, не заметите разницы, и, особенно в командной строке, иногда возможность легко вводить команду более важна.
здесь простая в использовании альтернатива, который, однако, является самый медленный подход; он использует упрощенный
ForEach-Object
синтаксис называется операции заявлением (опять же, PSv3+): ; например, следующее решение PSv3+ легко добавить к существующей команде:$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
для полноты картины: малоизвестный PSv4+
.ForEach()
метод сбора это еще одна альтернатива:# By property name (string): $objects.ForEach('Name') # By script block (much slower): $objects.ForEach({ $_.Name })
этот подход похож на член перечисление, С теми же компромиссами, за исключением того, что логика конвейера не применены; это чуть медленнее, хотя все еще заметно быстрее, чем трубопровод.
для извлечения одного значения свойства по имя (строка аргумент), это решение находится на одном уровне с перечислением членов (хотя последнее синтаксически проще).
в script-block вариант, хотя и намного медленнее, позволяет произвольно преобразования; это более быстрая-all-In-memory-at - once-альтернатива конвейерной
ForEach-Object
.
сравнение производительности различных подходов
здесь образец тайминги для различных подходов, основанных на входной коллекции
100,000
объекты, усредненные по 100 пробегам; абсолютные числа не важны и варьируются в зависимости от многих факторов, но это должно дать вам ощущение относительные производительность:Command FriendlySecs (100-run avg.) Factor ------- --------------------------- ------ $objects.ForEach('Number') 0.078 1.00 $objects.Number 0.079 1.02 foreach($o in $objects) { $o.Number } 0.188 2.42 $objects | Select-Object -ExpandProperty Number 0.881 11.36 $objects.ForEach({ $_.Number }) 0.925 11.93 $objects | % { $_.Number } 1.564 20.16 $objects | % Number 2.974 38.35
решение метода сбора на основе перечисления элементов / свойств на основе имен выполняется быстрее в 10 раз, чем самое быстрое решение на основе конвейера.
The
foreach
сообщении решение о 2.5 медленнее, но все же примерно в 4-5 раз быстрее, чем самое быстрое решение трубопровода.использование блок скрипта С решением метода сбора (
.ForEach({ ... }
) резко замедляет работу, так что он практически на одном уровне с самым быстрым решением на основе конвейера (Select-Object -ExpandProperty
).
% Number
(ForEach-Object Number
), что любопытно, выполняет хуже всего, хотя% Number
является концептуальным эквивалентом% { $_.Number }
).
исходный код для тестов:
Примечание: функция загрузки
Time-Command
С в этом суть для выполнения этих тестов.$count = 1e5 # input-object count (100,000) $runs = 100 # number of runs to average # Create sample input objects. $objects = 1..$count | % { [pscustomobject] @{ Number = $_ } } # An array of script blocks with the various approaches. $approaches = { $objects | Select-Object -ExpandProperty Number }, { $objects | % Number }, { $objects | % { $_.Number } }, { $objects.ForEach('Number') }, { $objects.ForEach({ $_.Number }) }, { $objects.Number }, { foreach($o in $objects) { $o.Number } } # Time the approaches and sort them by execution time (fastest first): Time-Command $approaches -Count $runs | Select Command, FriendlySecs*, Factor