1

This case only appears with arrays of the length one.

Example:

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

So I want $arrayAfterUnique to be an array of length one.
However, Select-Object will make a string out of it.

Is there an easy workaround for this problem?

2

3 Answers 3

3

However Select-Object will make a string out of it

Not quite - Select-Object will return 0 or more objects, and depending on the number, the PowerShell runtime will decide whether the result is to be stored as a scalar or an array.

You can force the result to be an array regardless of how many objects are in the output, by using the array sub-expression operator @():

$array = @("55") 
$arrayAfterUnique = @($array | Select-Object -Unique)
Sign up to request clarification or add additional context in comments.

2 Comments

What will happen if $array = @("55", "66")? Is it going to be a matrix then?
@Jenew: You'll get an [object[]] array (one-dimensional).
2

To complement Mathias R. Jessen's helpful answer, which explains the issue well:

An alternative to @(...) is to type-constrain the result variable with [array] placed to the left of the variable, in order to ensure that the collected result is always an array:

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

[array] is in effect the same as [object[]], i.e., a regular PowerShell array.

If the output from the RHS command is a single value (scalar), that value is now automatically converted to a single-element array; with multiple output values, the [object[]] array implicitly created by PowerShell for collecting the outputs is stored as-is.

Note: Type-constraining a variable means that future assignments too must be of the locked-in type or convertible to it; e.g., later execution of $arrayAfterUnique = 42 again converts integer 42 to a single-element array.

However, it is often not necessary to distinguish between scalars and arrays, because PowerShell allows you treat scalars like arrays, by given them a .Count property with value 1 and allowing indexing with [0] and [-1] - see this answer for more information.


As for the input side:

On sending arrays to the pipeline, PowerShell enumerates them - that is, the elements are sent one by one to the command in the next pipeline segment.

Therefore, single-value input is effectively the same as single-element array input.

That is, the following two commands behave identically:

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

If you need to ensure that an array is sent as a whole to the pipeline, precede it with , (the array-construction operator)[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] This wraps the array in an auxiliary single-element helper array, and it is that helper array that PowerShell enumerates, therefore sending its one and only element - the original array - as-is through the pipeline.

1 Comment

Thanks for this detailed explanation. This helps a lot!
1

You can add a custom function to convert to Array as shown here.

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

Usage:

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

Update:

Worth mentioning, that this approach might have performance penalties as a new array is being created for every item due to use of +-.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.