Converting to Array

Converting to Array

  • Comments 11

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.

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

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. :-)

$a = get-childitem | ToArray

- Marcel Ortiz Soto [MSFT]

P.S. if ToArray is too long for you, create an alias.

 

 

Leave a Comment
  • Please add 5 and 2 and type the answer here:
  • Post
  • 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).

    So I'm looking forward to the next post.

  • Here's an equally obscure, but more succinct, version:

    function ConvertTo-Array{END{ return ,@($input) }}

  • 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.

     -Marcel

  • What does

    return ,$output;

    mean ?

  • @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).

  • thanks totorocat for this clear explanation :)

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

  • @($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

  • Vladimir,

    It's "cristal" clear now

    thanks :)

  • An added bonus is that if you do a null check in the function you can avoid the extremely annoying behavior (sorry guys its one of the few "mistakes" I think) where the following outputs "bogus!"

    $a = Get-Process | ?{ $_.Name -eq 'Nobody' }

    $a | %{ "bogus!" }

  • If all you want it to ensure that the variable that holds the result from the pipe is an array, why not just run "$x=@($x)"?

    This should ensure that $x is an array while avoiding it becoming an array of arrays in case your pipe did return multiple objects.

    -Arvid

  • $x = @("a"); $x -is [string]; function y() { @("a") }; $x = y; $x -is [string]

    returns:

    False

    True

    Function return "unwinds" single element from array. So how to return an array with one entry from a function?

Page 1 of 1 (11 items)