Reputation: 12318
I have this script where I want to add an object to an array called $Target
in every foreach.
foreach ($Machine in $Machines)
{
$TargetProperties = @{Name=$Machine}
$TargetObject = New-Object PSObject –Property $TargetProperties
$Target= @()
$Target = $TargetObject
}
I know it is not working because $Target = $TargetObject
makes it equal to the same object.
How can I append to the array instead of replace?
Upvotes: 59
Views: 315629
Reputation: 1003
I know this is an old question but for future readers, the accepted answer is not ideal in every scenario especially when iterating over large sets of data within your loops.
The question is How to "Add objects to an array of objects". The title of this post vs the example problem given don't qualify for the same answer. Instead, I aim to address the problem demonstrated in the example.
While it's easy to use the append operator +=
with an array, behind the scene, it's quite a wasteful process. The extent of how wasteful depends on the size of the object you're looping through.
The accepted answer starts with an initialized empty array $Target = @()
. Then suggests looping through $Machines
appending items to the empty array using $Target += $TargetObject
. Behind the scene, PowerShell is measuring the size of $Target
and the size of the item you're appending $TargetObject
, adding the sizes together and creating a brand new array. It then copies/adds the contents from $Target
to the new array followed by the object you're appending. This process occurs on every item in the loop. If there are 1000 items in $Machines
, you will have created 1000 arrays by the end of the operation.
This all happens because plain old Arrays are a fixed size. By appending items to something that has a fixed size, we end up with this wasteful process. We can avoid this by not creating a new array at all. Instead, simply assign the array that foreach
loops already create to a variable. See the following for an example.
$Target = foreach ($Machine in $Machines)
{
# Stuff I want to be in $Target goes here
}
One of the other answers has the right idea but the example given is not correct. In order for anything to end up in the $Target
variable, you have to make sure the data is being sent to the Success output stream. Not to be confused with data being written to the Information stream when using something like Write-Host
.
Here is a working example:
# Mock array for illustration purposes
$Machines = @( "Machine1", "Machine2", "Machine3" )
$Target = foreach ( $Machine in $Machines )
{
# This is being sent directly to the success output stream and therefore, into the $Target variable
@{ Name = $Machine }
}
Your output should look like this:
Name | Value |
---|---|
Name | Machine1 |
Name | Machine2 |
Name | Machine3 |
If you're familiar with the Cmdlet Foreach-Object
, below is the equivalent. You can start to see the similarities with my first example.
$Target = $Machines | ForEach-Object { @{ Name = $_ } }
These articles go over everything I just mentioned as well as the performance impacts:
Upvotes: 18
Reputation: 27418
Typically people do this without using the inefficient "+=" that makes a new copy of the array every time:
$Target = foreach ($Machine in $Machines)
{
$TargetProperties = @{Name=$Machine}
$TargetObject = New-Object PSObject –Property $TargetProperties
}
Upvotes: 2
Reputation: 2621
To append to an array, just use the +=
operator.
$Target += $TargetObject
Also, you need to declare $Target = @()
before your loop because otherwise, it will empty the array every loop.
Upvotes: 120