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.
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 }
THANK YOU! So simple but it took me hours to find this!!
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 !
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!
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.
Thank you for this modification (using ArrayList). This has made this technique much more real world usable for me!
Thank you! Saved hours of work for me.
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.
Really useful. Thanks!
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.