3

In the simplified PS code below, I don't understand why the $MyPeople array gets changed after calling the changeData function. This array variable should just be made a copy of, and I expect the function to return another array variable into $UpdatedPeople and not touch $MyPeople:

function changeData {
    Param ([PSCustomObject[]]$people)
    $changed_people = $people
    $changed_people[0].Name = "NEW NAME"
    return $changed_people
}
# Original data:
$Person1 = [PSCustomObject]@{
    Name    = "First Person"
    ID      = 1
}
$Person2 = [PSCustomObject]@{
    Name    = "Second Person"
    ID      = 2
}
$MyPeople = $Person1,$Person2

"`$MyPeople[0] =`t`t" + $MyPeople[0]
"`n# Updating data..."
$UpdatedPeople  = changeData($MyPeople)
"`$UpdatedPeople[0] =`t" + $UpdatedPeople[0]
"`$MyPeople[0] =`t`t" + $MyPeople[0]

Console output:

$MyPeople[0] =          @{Name=First Person; ID=1}
# Updating data...
$UpdatedPeople[0] =     @{Name=NEW NAME; ID=1}
$MyPeople[0] =          @{Name=NEW NAME; ID=1}

Thanks!

Chris
  • 88
  • 1
  • 11
  • 1
    For conceptual background information on parameter passing in PowerShell, see [this answer](https://stackoverflow.com/a/60102611/45375). – mklement0 Mar 30 '20 at 17:07

2 Answers2

2

PSObject2 = PSObject1 is not a copy but a reference. You need to clone or copy the original object using a method designed for that purpose.

function changeData {
    Param ([PSCustomObject[]]$people)
    $changed_people = $people | Foreach-Object {$_.PSObject.Copy()}
    $changed_people[0].Name = "NEW NAME"
    return $changed_people
}

The technique above is simplistic and should work here. However, it is not a deep clone. So if your psobject properties contain other psobjects, you will need to look into doing a deep clone.

AdminOfThings
  • 23,946
  • 4
  • 17
  • 27
  • 1
    Thanks, now corrected. I thought I could accept the 2 answers... (clicked both, which removed the 1st) – Chris Mar 31 '20 at 08:37
1

We can clone the PSCustomObject. We will create a new PSObject and enumerate through the psobject given as parameter and add them one by one to the shallow copy.

function changeData {
    Param ([PSCustomObject[]]$people)
    $changed_people = New-Object PSobject -Property @{}
    $people.psobject.properties | ForEach {
      $changed_people | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value
    }
    $changed_people[0].Name = 'NEW NAME'
    return $changed_people
}

Or use another method by @AdminOfThings

Wasif
  • 14,755
  • 3
  • 14
  • 34
  • 1
    Thanks, however I prefer the `.PSObject.Copy()` method from @AdminOfThings as my objects have about 40 (non-PSObject) properties and could change in the future. – Chris Mar 30 '20 at 16:46