Converting to Array

When you run a PowerShell pipeline, that pipeline might return 0, 1 or many items. If you are assigning the results of that pipeline to a variable, you will get $null, 1 item or an array of items respectively. Sometimes, you won’t care about the different types returned, but at other times, you’ll actually prefer to force the results to always be an array. In those cases, you can use @( … ). As in:

$a = @(get-childitem)

That will evaluate the statements enclosed within the parentheses and collect the results into an array. If there are no results, then you’ll get an array of length zero.

This usually works well enough, but I found that I usually decide that I want an array after I’m well into writing the pipeline. I would then have to move the cursor back to the beginning of the pipeline, to insert the “@(“. After repeating that enough times I got annoyed and decided I’d rather just append something at the end of the pipeline that would convert the results to an array. Thus was born my ToArray function.

Simple enough right? The begin block creates a new empty array. The process block, which gets called for every pipeline item, adds that item to the array, and the end block just puts the array into an array of length 1 and writes that array to the pipeline. I need to wrap the $output array in a 1 element array because the pipeline will unravel all enumerables (well, almost all). This way it just unravels that 1 element array and what’s left is my original array.

One thing you should know is that this isn’t exactly the most performant way to do this. For every item I’m creating a brand new array. If I was going to pipe a lot of items I would probably be better off using an ArrayList and converting it to an array at the end. I’ll leave that part as an exercise for you.

This works for me. It’s simple and it makes my life easier. Now whenever I need to make the results into an array I just pipe into ToArray. No more having to to go back to the beginning of the pipeline for me. 🙂

I came here because I was trying to sort a hashtable using the $hash.keys string values, and a custom (non-dictionary) sort function that expects arrays.
However these keys are a collection, not an array. Very frustrating, and poor design IMO.

(I kept getting “Unable to index into an object of type System.Collections.Hashtable+KeyCollection”.
I could not find any solution anywhere to convert the collection to an array for sorting..)

Your solution was brilliant and works perfectly; I am surprised other people haven’t had to convert collections to arrays!
Thanks.

@($a) and ,$a do exactly the same when $a is a single object (not an array).

When $a is an array – @($a) is the same as $a. On the contrary, ,$a forces creation of an array regardless of whether $a is already an array or not. This results in creation of an array with one element, and this element is a nested array $a.

In this respect, if you are not sure if your output is an array or not but you want to make sure it is an array, use @($output). It will do nothing if $output is already an array, but will make it an array of one element if it is a single object.

Example:

PS> $a = 1,2,3

PS> (@($a)).length

3

PS> (,$a).length

1

PS> (,$a)[0].length

3

Hope this helps,

Vladimir Averkin

Windows Powershell Team

10 years ago

Stef

thanks totorocat for this clear explanation 🙂

However, I think I don’t understand the difference between @($output) and ,$output

10 years ago

totorocat

@Stef

The comma operator (",") is used to create arrays (e.g., "$a = 1,2,3" creates a three element array).

If you only have one element, you can just prefix it with the comma operator to create a single element array (e.g., "$a = ,1").

The line you quoted is wrapping the "$output" array into a single element array. This array-in-an-array will be ‘unwrapped’ when it’s returned from the pipeline, leaving just the inner array (i.e., "$output"). He does this to guarantee that he always gets an array.

If he didn’t do this, then the "$output" array would be ‘unwrapped’ itself; leaving either $null, a single item, or an array (with two or more items).

Bobhy, sorry it didn’t meet you expectations. In this case I merely wanted to share a function I’ve had in my profile for ages. However, it’s good to hear what you would like to learn more about. I’ll keep it in mind for future posts. If there are other topics you want to hear about, please let us know.

Joel, you’re right, it’s obscure, but it’s also a much better implementation since it removes the array resizing.

Sorry, I don’t think this post is up to the usually high quality of Powershell postings.

First off, it takes for granted an ugly feature of the language, the indeterminancy of result type depending on runtime results. I would have loved to read a justification of why it has to work that way, this would have whetted my appreciation for the workaround.

Next, the workaround is a fix for a relatively trivial aspect of the problem, the desire to avoid a few keystrokes, and a low performance and clunky fix at that (the return *,* $output is a master-stroke of opacity).