Допустим, у нас есть массив объектов $ objects. Допустим, у этих объектов есть свойство «Имя».
Это то, чем я хочу заниматься
$results = @()
$objects | %{ $results += $_.Name }
Это работает, но можно ли сделать это лучше?
Если я сделаю что-то вроде:
$results = objects | select Name
$results
представляет собой массив объектов, имеющих свойство Name. Я хочу, чтобы $ results содержал массив имен.
Есть ли способ лучше?
arrays
powershell
member-enumeration
Сильвен Реверди
источник
источник
$results = @($objects | %{ $_.Name })
. Иногда это может быть удобнее вводить в командной строке, хотя я думаю, что ответ Скотта в целом лучше.$objects | % Name
Ответы:
Я думаю, вы могли бы использовать
ExpandProperty
параметрSelect-Object
.Например, чтобы получить список текущего каталога и просто отобразить свойство Name, нужно сделать следующее:
Это все еще возвращает объекты DirectoryInfo или FileInfo. Вы всегда можете проверить тип, проходящий через конвейер, связавшись с Get-Member (псевдоним
gm
).Итак, чтобы расширить объект до типа свойства, на которое вы смотрите, вы можете сделать следующее:
В вашем случае вы можете просто сделать следующее, чтобы переменная была массивом строк, где строки - это свойство Name:
источник
В качестве еще более простого решения вы можете просто использовать:
Который должен заполнить
$results
массивом всех значений свойства Name элементов в$objects
.источник
Exchange Management Shell
. При использовании Exchange нам необходимо использовать$objects | select -Property Propname, OtherPropname
Чтобы дополнить уже существующие полезные ответы, указав, когда какой подход использовать, и сравнением производительности .
Вне конвейера используйте (PSv3 +):
как показано в ответе rageandqq , который синтаксически проще и намного быстрее .foreach
оператор , вывод которого вы также можете напрямую присвоить переменной:(Get-ChildItem).Name
), эта команда должна сначала выполняться до завершения, прежде чем можно будет получить доступ к элементам результирующего массива.В конвейере, где результат должен быть обработан дальше или результаты не помещаются в память в целом, используйте:
-ExpandProperty
объясняется в ответе Скотта Саада .Для небольших входных коллекций (массивов) вы, вероятно, не заметите разницы , и, особенно в командной строке, иногда более важно иметь возможность легко ввести команду.
Вот альтернатива , которую легко набрать , но это самый медленный подход ; он использует упрощенный
ForEach-Object
синтаксис, называемый оператором операции (опять же, PSv3 +):; например, следующее решение PSv3 + легко добавить к существующей команде:Ради полноты: малоизвестной PSv4 +
.ForEach()
метод массива , более comprehensivel обсуждается в этой статье , это еще одна альтернатива :Этот подход аналогичен перечислению членов с теми же компромиссами, за исключением того, что конвейерная логика не применяется; он немного медленнее , но все же заметно быстрее конвейера.
Для извлечения одного значения свойства по имени ( строковый аргумент) это решение соответствует перечислению членов (хотя последнее синтаксически проще).
Скрипт-блок - вариант , позволяет произвольные преобразования ; это более быстрая альтернатива
ForEach-Object
cmdlet (%
), работающая по принципу « все в памяти» .Сравнение эффективности различных подходов
Вот примерное время для различных подходов, основанное на входной коллекции
10,000
объектов , усредненных по 10 запускам; Абсолютные числа не важны и зависят от многих факторов, но они должны дать вам представление об относительной производительности (тайминги взяты с одноядерной виртуальной машины Windows 10:Важный
Относительная производительность зависит от того, являются ли входные объекты экземплярами обычных типов .NET (например, в качестве вывода
Get-ChildItem
) или[pscustomobject]
экземплярами (например, как выходные данныеConvert-FromCsv
).Причина в том, что
[pscustomobject]
свойства динамически управляются PowerShell, и он может получить к ним доступ быстрее, чем обычные свойства (статически определенного) обычного типа .NET. Оба сценария описаны ниже.Тесты используют коллекции, уже находящиеся в памяти, в качестве входных данных, чтобы сосредоточиться на производительности чистого извлечения свойств. При потоковом вызове командлета / функции в качестве входных данных различия в производительности, как правило, будут гораздо менее выраженными, поскольку время, проведенное внутри этого вызова, может составлять большую часть затраченного времени.
Для краткости
%
дляForEach-Object
командлета используется псевдоним .Общие выводы , применимые как к обычному типу .NET, так и к
[pscustomobject]
вводу:Member-enumeration (
$collection.Name
) иforeach ($obj in $collection)
решения являются самыми быстрыми , в 10 или более раз быстрее, чем самое быстрое решение на основе конвейера.Удивительно, но
% Name
работает намного хуже, чем% { $_.Name }
- см. Эту проблему на GitHub .PowerShell Core здесь неизменно превосходит Windows Powershell.
Тайминги с обычными типами .NET :
Выводы:
.ForEach('Name')
явно превосходит.ForEach({ $_.Name })
. Любопытно, что в Windows PowerShell последняя работает быстрее, хотя и незначительно.Тайминги с
[pscustomobject]
экземплярами :Выводы:
Обратите внимание , как при
[pscustomobject]
входе.ForEach('Name')
на сегодняшний день превосходит скрипт-блок на основе варианта,.ForEach({ $_.Name })
.Точно так же
[pscustomobject]
ввод делает конвейерSelect-Object -ExpandProperty Name
быстрее, в Windows PowerShell практически на одном уровне.ForEach({ $_.Name })
, но в PowerShell Core по-прежнему примерно на 50% медленнее.Вкратце: за странным исключением
% Name
,[pscustomobject]
строковые методы ссылки на свойства превосходят методы на основе сценариев.Исходный код для тестов :
Примечание:
Загрузите функцию
Time-Command
из этого Gist, чтобы запустить эти тесты.Вместо этого установите
$useCustomObjectInput
для$true
измерения с[pscustomobject]
экземплярами.источник
Внимание! Перечисление членов работает, только если сама коллекция не имеет члена с таким же именем. Итак, если у вас есть массив объектов FileInfo, вы не можете получить массив длин файлов, используя
И прежде чем вы скажете «хорошо, очевидно», подумайте об этом. Если у вас был массив объектов со свойством емкости, тогда
будет работать нормально, ЕСЛИ $ objarr на самом деле не [Array], а, например, [ArrayList]. Поэтому перед использованием перечисления членов вам, возможно, придется заглянуть внутрь черного ящика, содержащего вашу коллекцию.
(Примечание для модераторов: это должен быть комментарий к ответу rageandqq, но у меня еще недостаточно репутации.)
источник
.ForEach()
метода массива следующим образом:$files.ForEach('Length')