×

IDMWORKS Blog

What is the most efficient way to create a collection of objects in PowerShell?


The Powershell Mantra
Try not to use a module and filter the pipeline from left to right.

Use Case
Create a bucket of objects to add other objects; AND create/delete additional columns (properties) as needed

EXAMPLE DATA FOR COMPARISON

$foo = dir -Path $env:ProgramFiles -Recurse

$files = $foo.Where({$_.PSIsContainer -eq $false})

$files.count = 18645




4 Approaches for Bucket Creation

1) Adding array objects via the += Operator

$Combined = @()

$files | ForEach-Object{

    $obj = New-Object PSCustomObject

    $obj | Add-Member -MemberType NoteProperty -Name FileName -value $_.fullname

    $obj | Add-Member -MemberType NoteProperty -Name LastWrite -value $_.Lastwritetime

    $Combined += $obj

}

Result:  TotalSeconds      : 19.38

What we’d really like to do in the last line of the above example is add the $obj object to $combined: $Combined.Add($obj).  If you try this, you’ll receive this error:
Exception calling “Add” with “1” argument(s): “Collection was of a fixed size.” 

If you resort to using the += operator to add an element to an array, PowerShell actually creates a new array with the values of the original array and the added value.

2) Systems.Collections.ArrayList with New-Object and Add-Member PS modules

 [System.Collections.ArrayList]$Combined2 = @()

$files | ForEach-Object{

    $obj = New-Object PSCustomObject

    $obj | Add-Member -MemberType NoteProperty -Name FileName -value $_.fullname

    $obj | Add-Member -MemberType NoteProperty -Name LastWrite -value $_.Lastwritetime

    $combined2.Add($obj)|Out-Null

}

Result: TotalSeconds      : 6.74

System.Collections.ArrayList
is the perfect bucket but we’re using costly modules such as New-Object and Add-Member

3) Systems.Collections.ArrayList with inline PScustomObject definition

[System.Collections.ArrayList]$Combined3 = @()

$files | ForEach-Object{

    $obj = [PSCustomObject]@{

        FileName  = $_.fullname

        LastWrite = $_.Lastwritetime

    }

    $Combined3.Add($obj)|Out-Null

}

Result: TotalSeconds      : 1.67

In this approach, the inline method using only one module
(ForEach-Object) and is over 5 times faster.

 4)
Systems.Collections.ArrayList with inline PScustomObject definition and NO MODULES

[System.Collections.ArrayList]$Omega = @()

$files.ForEach({$obj = [PSCustomObject]@{ FileName  = $_.fullname;

LastWrite = $_.Lastwritetime};$Omega.add($obj)|out-null})}

Result: TotalSeconds      : 1.26

It’s ugly to read, but it’s fast.

 

Conclusion
I’ve demonstrated that our mantra works.

The System.Collection.ArrayList is the perfect bucket because it permits you to add objects as a method and supports heterogeneous object types.

Using the Inline PScustomObject declaration approach also eliminates our need for using other modules. 

I’d recommend you use approach three until you’re comfortable with the concept.  But keep the forth approach in the back of your mind, should you ever need the extra speed.

Questions, comments or concerns? Feel free to reach out to us below, or email us at IDMWORKS to learn more about how you can protect your organization and customers.

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *