I've read the PowerShell Team blog post Converting to Array and I've implemented the ToArray function it describes. This works. The article mentions performance could be improved by using an ArrayList
instead of an array but doesn't include code for the ArrayList
version. Now I'm trying to implement an ArrayList
version of the function. However, I cannot get this to work.
In the End block of the pipeline function I convert the ArrayList
to an array, then pass that array out. When the array is within the End block the array elements are of the right type and value. However, once the array is passed out of the End block the format of the array changes. The array returned to the calling code appears to combine an array of integer values (maybe index values?) with the array converted from the ArrayList
. What am I doing wrong?
Here's the original version of the ToArray function, more-or-less a copy of the code from the blog post. It works:
function Convert-PipelineOutputToArray
{
Begin
{
$output = @();
}
Process
{
$output += $_;
}
End
{
# Wraps the output in a one-element array to prevent the pipeline from unrolling the
# output. In this case the pipeline will unroll the one-element wrapper array, leaving
# the original output array.
return ,$output;
}
}
$result = Get-Service | Convert-PipelineOutputToArray
Write-Host "Number of elements: $($result.Count)"
Write-Host "Resulting type: $($result.GetType().FullName)"
Write-Host "Element type: $($result[0].GetType().FullName)"
Write-Host "First 5 elements:"
for($i = 0; $i -le 5; $i++)
{
Write-Output $result[$i]
}
Result:
Number of elements: 353
Resulting type: System.Object[]
Element type: System.ServiceProcess.ServiceController
First 5 elements:
Status Name DisplayName
------ ---- -----------
Stopped AarSvc_2e4aa3 Agent Activation Runtime_2e4aa3
Stopped acswgagent Cisco AnyConnect SWG Agent
Running acumbrellaagent Cisco AnyConnect Umbrella Roaming Sec…
Running AdobeARMservice Adobe Acrobat Update Service
Stopped AJRouter AllJoyn Router Service
Stopped ALG Application Layer Gateway Service
And here's the modified version of the function using an ArrayList
:
function Convert-PipelineOutputToArray
{
Begin
{
$arrayList = New-Object System.Collections.ArrayList;
}
Process
{
$arrayList.Add($_);
}
End
{
$outputArray = $arrayList.ToArray();
Write-Host "End block - number of elements: $($outputArray.Length)";
Write-Host "End block - element type: $($outputArray[0].GetType().Name)";
Write-Host "End block - first element value: $($outputArray[0].Name)";
Write-Host "End block -------------";
return ,$outputArray;
}
}
$result = Get-Service | Convert-PipelineOutputToArray
Write-Host "Number of elements: $($result.Count)"
Write-Host "Resulting type: $($result.GetType().FullName)"
Write-Host "Element type: $($result[0].GetType().FullName)"
Write-Host "First 5 elements:"
for($i = 0; $i -le 5; $i++)
{
Write-Output $result[$i]
}
Result:
End block - number of elements: 353
End block - element type: ServiceController
End block - first element value: AarSvc_2e4aa3
End block -------------
Number of elements: 354
Resulting type: System.Object[]
Element type: System.Int32
First 5 elements:
0
1
2
3
4
5
I noticed the $result
array has 354 elements while the $outputArray
in the End block of the function had only 353. Investigating the $result
array I discovered elements [0] to [352] were integers and element [353] was a sub-array of 353 ServiceController
objects - the output I wanted.
I tried modifying the return statement in the End block to leave out the leading comma: return $outputArray;
. That changes the number of elements in the $result
array to 706, with elements [0] to [352] being integers and elements [353] to [705] being the ServiceController
objects. So it obviously unrolls the sub-array containing the ServiceController
objects into the main $result
array.
How can I return just the array of ServiceController
objects from the End block?