Nick
Nick

Reputation: 1208

PowerShell Hashtable values being removed from second hashtable outside of loop

I'm sure that title isn't very clear, and for that I apologize. I've run into a bit of an issue with a script that I'm writing and am hoping for some assistance figuring out what is going on. I figured out how to resolve the issue, I'm more looking for why this is happening. The script is over 3k lines long, but the snippet I wrote below reproduces the same issue.

I have three hash tables, DeviceList, AllDevices and RemovedDevices. Devicelist is laid out explicitly, and AllDevices equals DeviceList. Within a loop, items that exist in RemovedDevices are removed from DeviceList. Even though AllDevices is only modified at the beginning in this sample, the devices are still being removed from it.

$DeviceList = @{"server1" = "email1"; "server2" = "email2"; "server3" = "email3"}
$AllDevices = $DeviceList
$RemovedDevices = @{"server1" = "email1"; "server2" = "email2"}

foreach($RemovedDevice in $RemovedDevices.GetEnumerator())
  {
  $DeviceList | where {$_.ContainsKey($RemovedDevice.Key)} | % {$_.Remove($RemovedDevice.Key)}
  }
$AllDevices

Running the above text, $AllDevices is modified to only contain the server3 info, where it should still contain all 3 servers.

If I modify the second line to:

$AllDevices += $DeviceList

Then AllDevices maintains all 3 values. Using breakpoints and stepping through on the first version, I have verified that AllDevices is not being hit after the first time.

My overall question is: Why is AllDevices being modified to equal DeviceList after the loop has finished? If it's only called once at the beginning, shouldn't it's value remain static despite modifications to DeviceList? Rebuilding an array using a similar method doesn't overwrite AllDevices so I imagine it's a hashtable quirk.

PSVersion 5.0.10586.117

Upvotes: 2

Views: 290

Answers (1)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174575

Why is AllDevices being modified to equal DeviceList after the loop has finished?

Because $AllDevices is just a reference to the same underlying object as $DeviceList

If it's only called once at the beginning, shouldn't it's value remain static despite modifications to DeviceList?

If $AllDevices was an identical object, sure, but it is not just identical - it is the same object.

Fortunately, hashtables implement the ICloneable interface, so for shallow hashtables, you can use the Clone() method:

$DeviceList = @{"server1" = "email1"; "server2" = "email2"; "server3" = "email3"}
$AllDevices = $DeviceList.Clone()

$RemovedDevices = @{"server1" = "email1"; "server2" = "email2"}

foreach($RemovedDevice in $RemovedDevices.GetEnumerator())
{
    if($DeviceList.ContainsKey($RemovedDevice.Key))
    {
        $DeviceList.Remove($RemovedDevice.Key)
    }
}
$AllDevices

I found the usage of a hashtable with Where-Object super unreadable (not to mention unnecessarily slow), but functionally the above is the same as your original foreach loop body

Upvotes: 4

Related Questions