Select-Object -Unique возвращает String вместо массива String - PullRequest
1 голос
/ 04 ноября 2019

Этот случай появляется только с массивами длины один.

Пример:

$array = @("55") 
$arrayAfterUnique = $array | Select-Object -Unique 

Итак, я хочу, чтобы $arrayAfterUnique был массивом длины 1.
Однако Select-Object сделает из него строку.

Есть ли простой способ обойти эту проблему?

Ответы [ 3 ]

2 голосов
/ 04 ноября 2019

Однако Select-Object сделает из него строку

Не совсем - Select-Object вернет 0 или более объектов , и в зависимости от числасреда выполнения PowerShell решит, будет ли результат сохранен в виде скаляра или массива.

Вы можете сделать так, чтобы результат был массивом независимо от того, сколько объектов находится в выходных данных, с помощью подпрограммы массиваоператор @():

$array = @("55") 
$arrayAfterUnique = @($array | Select-Object -Unique)
0 голосов
/ 04 ноября 2019

В дополнение к Полезный ответ Матиаса Р. Йессена , который хорошо объясняет проблему:

Альтернативой @(...) является ограничение типа переменная результата с [array], помещенная в слева переменной, чтобы гарантировать, что собранный результат будет всегда массивом:

# Ensure that $arrayAfterUnique always contains an array.
[array] $arrayAfterUnique = 1, 1 | Select-Object -Unique 

[array] фактически совпадает с [object[]], то есть с обычным массивом PowerShell.

Если вывод команды RHS представляет собой одно значение (скалярное), тозначение теперь автоматически конвертируется в одноэлементный массив;с несколькими выходными значениями массив [object[]], неявно созданный PowerShell для сбора выходных данных, сохраняется как есть.

Примечание. Ограничение типа переменной означает, что будущие назначения тоже должны быть заблокированного типаили конвертируемые в него;Например, последующее выполнение $arrayAfterUnique = 42 снова преобразует целое число 42 в одноэлементный массив.

Однако часто не обязательно , чтобы различать скаляры и массивы , поскольку PowerShell позволяет обрабатывать скаляры как * массивы , предоставляя им свойство .Count со значением 1 и позволяя индексировать с помощью [0] и [-1] - см. этот ответ для получения дополнительной информации.


Что касается стороны input :

При отправке массивов в конвейер, PowerShell перечисляет их - то есть элементы отправляются один за другим команде в следующем сегменте конвейера.

Поэтому, однозначное входное значение фактически совпадает с одноэлементным массивом входное значение.

То есть следующие две команды ведут себя одинаково:

# In both cases, Select-Object receives single value '55'
  '55'  | Select-Object -Unique   # single value
@('55') | Select-Object -Unique   # single-element array

Если вам нужно убедиться, что массив отправляется в целом в конвейер , pубрать его с помощью , (оператор построения массива) [1] :

# Send array 1, 2 as a whole to ForEach-Object
# Note the need for (...) to clarify precedence.
, (1, 2) | ForEach-Object { $_.GetType().Name } # -> 'Object[]'

# Less efficient, but conceptually clearer alternative:
Write-Output -NoEnumerate 1, 2

[1] Это оборачивает массив во вспомогательный одно-вспомогательный массив элементов, и именно этот вспомогательный массив перечисляет PowerShell, поэтому отправляет свой единственный элемент - исходный массив - как есть через конвейер.

0 голосов
/ 04 ноября 2019

Вы можете добавить пользовательскую функцию для преобразования в массив , как показано здесь .

function ToArray
{
  begin
  {
    $output = @()
  }
  process
  {
    $output += $_
  }
  end
  {
    return ,$output
  }
}

Использование:

$array = @("55") 
$arrayAfterUnique = $array | Select-Object -Unique | ToArray
$arrayAfterUnique.GetType() # Returns System.Array

Обновление:

Стоит отметить, что этот подход может привести к снижению производительности, поскольку для каждого элемента создается новый массив из-за использования +-.

...