« Using C# To Draw An OrgChart in Visio 2010 | Main | BioShock Infinite Screenshots »
Sunday
May262013

Returning Collections from Functions in PowerShell: A tip for C# Developers learning PowerShell  

Previously in in April, I mentioned something C# developers should pay attention to when they start writing PowerShell code. Now, I want share another common PowerShell behavior that will be confusing at first for a C# developer and how to handle it. 

Let's start with this code:

 

$a = New-Object 'System.Collections.Generic.List[string]'
$a.Add("A")
$a.Add("B")
$a.Add("C")
$a.GetType().Name

 

 

What do you think will be printed by the last line? If you said "List<string>" you are correct –  however, it is displayed in a very ugly way:

List`1

Let's try another case:

$table = New-Object system.Data.DataTable
$col1 = $table.Columns.Add( "Col1", [string])
$col2 = $table.Columns.Add( "Col2", [int])
$table.Rows.Add( "bar", 0 )
$table.Rows.Add( "beer", 1 )
$table.Rows.Add( "baz", 2 )
$table.GetType().Name 

As expected the last line prints:

DataTable

Using Functions

Let's put that same code into functions:

function f1()
{
$arr = New-Object 'System.Collections.Generic.List[string]'
$arr.Add("A")
$arr.Add("B")
$arr.Add("C")
return $arr
}

function f2()
{
$table = New-Object system.Data.DataTable
$col1 = $table.Columns.Add( "Col1", [string]) | Out-Null
$col2 = $table.Columns.Add( "Col2", [int]) | Out-Null
$table.Rows.Add( "bar", 0 ) | Out-Null
$table.Rows.Add( "beer", 1 ) | Out-Null
$table.Rows.Add( "baz", 2 ) | Out-Null
return $table
}

Let's call those functions and check the return type

$a = f1
$a.GetType().Name
$t = f2
$t.GetType().Name

This piece of code will print out two things. Based on our previous experience we will expect to see:

List`1
DataTable

Here's what we actually see:

Object[]
Object[]

What happened?

If you come from a C# background this is very surprising. All I can say is that PowerShell doesn't always follow the semantics and behavior we are used to.

In this case, when a PowerShell function detects that you are retuning a enumerable object (such as a collection), it places each member of the collection on the pipeline as a separate item which in turn get collected into an Object[]

 

The Solution

This is going to look ugly, but the way to get around this is to return your collections as a single element array.

 

function f1()
{
$arr = New-Object 'System.Collections.Generic.List[string]'
$arr.Add("A")
$arr.Add("B")
$arr.Add("C")
return @(,$arr)
}

function f2()
{
$table = New-Object system.Data.DataTable
$col1 = $table.Columns.Add( "Col1", [string]) | Out-Null
$col2 = $table.Columns.Add( "Col2", [int]) | Out-Null
$table.Rows.Add( "bar", 0 ) | Out-Null
$table.Rows.Add( "beer", 1 ) | Out-Null
$table.Rows.Add( "baz", 2 ) | Out-Null
return @(,$table)
}

And now we will get what we expect:

List`1
DataTable

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (1)

Thanks! Not too new to Powershell, but just now stumbled on this issue, because I was trying to access a property on the collection object, so your post helped me - greatly appreciated!

You might expand this a bit, listing a reason or two why this might be beneficial or necessary. Also slightly interesting is that I believe the reason this trick works is more or less the opposite of the reason it is necessary - please confirm this:

As you described, when you return a collection type object (in my case a Microsoft.Exchange.WebServices.Data.FindItemsResults<TItem>), I think Powershell will enumerate it to produce an array for the pipeline

However, if you return a simple array (or I believe even a collection) with only a single item, it will unwrap it and just return only the single item (thus, an array or collection of more than one string is returned as an array of strings, but an array of a single string is unwrapped and returned as a single string).

Since it does not do this recursively, this trick works because it will unwrap the array containing the single collection and return that one item.

Assuming I'm even right, I might not be telling you anything new, especially since this post is more than 2 years old, but someone might benefit from the explanation(?)

August 14, 2015 | Unregistered CommenterBrett Peirce

PostPost a New Comment

Enter your information below to add a new comment.
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>