PowerShell – Create collections of custom objects

Background

Today a colleague asked my how he could store object collections in memory (a PowerShell variable) instead of writing and reading to/from CSV files. While searching for it he found tons of examples but most were written specifically for one target, he needed something more basic and flexible. He asked me if I already had a topic about it on my blog. Sadly I had to disappoint him, it wasn’t on my blog, yet. However I knew the answer and I will now also share this on my blog.

Creating the collection

Lets start of with creating an ArrayList in PowerShell:

$collectionVariable = New-Object System.Collections.ArrayList

Done. This is our generic collection, it can contain any PowerShell (.NET) object.

Before adding items to the collection be very aware that the fields of the first item added dictate which fields the collection will have.

For example take object $A and object $B.
Object $A has two string fields: “fieldA” and “fieldB”
Object $B also has two string fields: “fieldB” and “fieldC”
If object $A is added to our new empty collection, the collection would then have two fields: “fieldA” and “fieldB”.
If we would then add $B to the same collection the item in the collection would have an empty value in the field “fieldA” and no field “fieldC” (fieldB would be added normally to the list).
Keep this in mind when adding different types of objects to a collection.

Creating a custom object

Creating a custom object is easy:

$item = New-Object System.Object

This creates an empty System.Object object. This Object has no fields and only has four methods:
bool Equals(System.Object obj)
int GetHashCode()
type GetType()
string ToString()

This makes it an ideal object to start with as we can manually define every field.

So how do we add fields to our empty object?

Like this:

$item | Add-Member -MemberType NoteProperty -Name "Field1" -Value "value"

This example create a field named “Field1” with the value “value”, you can also pass a variable as value or even a field of a different object. For adding multiple field just repeat the line with different “Name” values.

This method can also be used to add fields to existing objects. For example you can read a csv file, add fields (for example a calculated field based on values of other fields) to the objects and then add all of those to a new (empty) collection which you can then write to a csv again or process further.

Adding the custom object to the ArrayList

We now have an ArrayList and need to put our custom object in it.

For people who are used to .NET and the way the lists work the method will be mostly unsurprising. There is only one thing to keep in mind, the Add method returns the index for the new item in the array. If you do not need this (and don’t want a series of indexes appearing on the console) you could output the result to null as in below example:

$collectionVariable.Add($object) | Out-Null

This will add the custom object to our ArrayList and will ignore the returned value.

Putting it all together

For this example I add ten objects to an ArrayList; the ten objects are the same but you can modify this to your own specific situation.

$collectionWithItems = New-Object System.Collections.ArrayList
for($i = 0; $i -lt 10; $i++)
{
    $temp = New-Object System.Object
    $temp | Add-Member -MemberType NoteProperty -Name "Field1" -Value "Value1"
    $temp | Add-Member -MemberType NoteProperty -Name "Field2" -Value "Value2"
    $temp | Add-Member -MemberType NoteProperty -Name "Field3" -Value "Value3"
    $collectionWithItems.Add($temp) | Out-Null
}

If I would then call $collectionWithItems it will return the collection. This is what the output is of $collectionWithItems when called after the for loop:

Field1                      Field2                      Field3
------                      ------                      ------
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3
Value1                      Value2                      Value3

Update (2017-06-13): Quicker/dirtier way to create objects with certain fields

Another way (though less pretty) is to do a select statement on any object, this will create a PSCustomObject with only the selected properties. Instead of the above example where it took 4 lines to create an object with 3 properties, this object can be created with all three fields in 1 line. However adding the contents to the fields might still require some additional lines which makes it also end up with 4 lines to create and fill the object.

$collectionWithItems = New-Object System.Collections.ArrayList
for($i = 0; $i -lt 10; $i++)
{
    $temp = "" | select "Field1", "Field2", "Field3"
    $temp.Field1 = "Value1"
    $temp.Field2 = "Value2"
    $temp.Field3 = "Value3"
    $collectionWithItems.Add($temp) | Out-Null
}

 

12 thoughts on “PowerShell – Create collections of custom objects

  1. Same for me… Was looking for hours to find a solution !

    All I wanted to do is print in a file table style :

    $collectionWithItems = @()

    For ($i=0; $i -le $dsv.Schema.Tables.Count-1; $i++)
    {
    if ($dsv.schema.Tables[$i].ExtendedProperties[“TableType”] -eq “View”)
    {
    $j++

    $strings1 = $j.ToString()
    $strings2 = $dsv.schema.Tables[$i].tablename
    $strings3 = $dsv.schema.Tables[$i].ExtendedProperties[“FriendlyName”]

    $temp = New-Object System.Object
    $temp | Add-Member -MemberType NoteProperty -Name “No” -Value $strings1
    $temp | Add-Member -MemberType NoteProperty -Name “TableName” -Value $strings2
    $temp | Add-Member -MemberType NoteProperty -Name “FriendlyName” -Value $strings3
    $collectionWithItems += $temp
    }
    }
    Write-Output $collectionWithItems | ft -AutoSize | Out-File $File -append

    Your solution did exactly what I wanted

    Thanks !

  2. This was so valuable as I now can produce object collections easily from my functions to pass all the values I want to the main body of the script and maintain the structure of the object as it passes from the function. Thanks very much for your contribution!

  3. Does this end up allocating a new array each time += is called? Seems like it could get expensive if iterating tens of thousands of objects.

    • You are absolutely right! I usually don’t use this for larger numbers, as this would indeed create a new array every time an item is added. I have changed the article to use an ArrayList instead. This will grow similar to a regular list, doubling in size once it runs out of space; which should offer better performance even when dealing with larger numbers.

  4. An impressive share! I have just forwarded this onto a colleague who has
    been doing a little homework on this. And he in fact ordered
    me dinner because I found it for him… lol.
    So let me reword this…. Thank YOU for the meal!! But yeah, thanks for spending time
    to discuss this matter here on your website.

  5. Hello,

    Thank you for this.

    What is the version of Powershell ? On my 5.1 this script :
    $usersToDisable = New-Object System.Collections.ArrayList

    $userToAdd = New-Object System.Object
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘Name’ -Value ‘le nom’
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘DistinguishedName’ -Value ‘le dn’
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘WhenCreated’ -Value (Get-Date -Format ‘dd/MM/yyyy’)
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘LastLogonDate’ -Value (Get-Date -Format ‘dd/MM/yyyy’)
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘PwdLastSet’ -Value (Get-Date -Format ‘dd/MM/yyyy’)
    $usersToDisable.Add($userToAdd) | Out-Null

    $userToAdd = New-Object System.Object
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘Name’ -Value ‘le nom2’
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘DistinguishedName’ -Value ‘le dn2’
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘WhenCreated’ -Value (Get-Date -Format ‘dd/MM/yyyy’)
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘LastLogonDate’ -Value (Get-Date -Format ‘dd/MM/yyyy’)
    $userToAdd | Add-Member -MemberType NoteProperty -Name ‘PwdLastSet’ -Value (Get-Date -Format ‘dd/MM/yyyy’)
    $usersToDisable.Add($userToAdd) | Out-Null

    $usersToDisable

    Returns :
    Name : le nom
    DistinguishedName : le dn
    WhenCreated : 22/09/2023
    LastLogonDate : 22/09/2023
    PwdLastSet : 22/09/2023

    Name : le nom2
    DistinguishedName : le dn2
    WhenCreated : 22/09/2023
    LastLogonDate : 22/09/2023
    PwdLastSet : 22/09/2023

    And not something like that :

    Name DistinguishedName WhenCreated LastLogonDate PwdLastSet
    ——– ———————– ——————- ——————– —————
    le nom le dn 22/09/2023 22/09/2023 22/09/2023
    le nom2 le dn2 22/09/2023 22/09/2023 22/09/2023

    • Hi Samuel,

      It seems your standard output is to show it is a list, if you do this line:
      $usersToDisable | Format-Table
      It should show it in table form, you can also choose to not show all values for example:
      $usersToDisable | Select “Name”, “WhenCreated” | Format-Table

      If in some case you do want to have it shown as a list instead of a table, you can simply replace Format-Table with Format-List in the examples above here.

Leave a Reply

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