I am trying to process some data in an ordered dictionary, then add that to another ordered dictionary, and I can do that by reinitializing my temporary dictionary, like this...
$collection = [Collections.Specialized.OrderedDictionary]::new()
foreach ($id in 1..5) {
$tempCollection = [Collections.Specialized.OrderedDictionary]::new()
foreach ($char in [Char]'a'..[Char]'e') {
$letter = ([Char]$char).ToString()
if ($id % 2 -eq 0) {
$letter = $letter.ToUpper()
}
$int = [Int][Char]$letter
$tempCollection.Add($letter, $int)
}
$collection.Add($id, $tempCollection)
}
foreach ($id in $collection.Keys) {
Write-Host "$id"
foreach ($key in $collection.$id.Keys) {
Write-Host " $key : $($collection.$id.$key)"
}
}
However, I feel like reinitializing is a bit inefficient/inelegant, and I would rather just .Clear()
that temporary variable. Which leads to this...
$collection = [Collections.Specialized.OrderedDictionary]::new()
$tempCollection = [Collections.Specialized.OrderedDictionary]::new()
foreach ($id in 1..5) {
foreach ($char in [Char]'a'..[Char]'e') {
$letter = ([Char]$char).ToString()
if ($id % 2 -eq 0) {
$letter = $letter.ToUpper()
}
$int = [Int][Char]$letter
$tempCollection.Add($letter, $int)
}
$collection.Add($id, $tempCollection)
$tempCollection.Clear()
}
foreach ($id in $collection.Keys) {
Write-Host "$id"
foreach ($key in $collection.$id.Keys) {
Write-Host " $key : $($collection.$id.$key)"
}
}
The problem is that while simple objects like string, int, char, etc are passed by value, all complex objects like a dictionary are passed by reference. So I pass the SAME dictionary in every iteration of $collection.Add($id, $tempCollection)
and the final state of $tempCollection
is cleared, so the result is 5 empty members of $collection
.
I know I can force something that is normally passed By Value to be By Reference using [Ref]
as outlined here. And [Ref]
is just an accelerator for System.Management.Automation.PSReference
. So what I need is a way to force an argument By Value, but neither [Val]
nor [ByVal]
works, and searching for System.Management.Automation.PSValue
doesn't seem to return anything useful either. The PSReference doco linked above says
This class is used to describe both kinds of references:
a. reference to a value: _value will be holding the value being referenced.
b. reference to a variable: _value will be holding a PSVariable instance for the variable to be referenced.
which makes me think I can get to the Value somehow, but for the life of me I can't grok HOW. Am I on the right track, and just missing something, or am I misunderstanding this documentation completely?
Cloning also seems like a potential solution, i.e. $collection.Add($id, $tempCollection.Clone())
, but Ordered Dictionaries don't implement ICloneable. .CopyTo()
also isn't an option, since it doesn't necessarily maintain the order of the elements. Nor does .AsReadOnly()
since
The AsReadOnly method creates a read-only wrapper around the current OrderedDictionary collection. Changes made to the OrderedDictionary collection are reflected in the read-only copy. Nor does OrderedDictionary implement
.copy()
as PSObject does.
I also tried making a new variable, like this...
$newCollection = $tempCollection
$collection.Add($id, $newCollection)
$tempCollection.Clear()
And that doesn't work either. So it seems that complex objects by reference seems to apply to more than just passed arguments.
It seems almost like my Ordered Dictionary choice/need is the root of the problem, but it seems like needing a unconnected copy of an Ordered Dictionary would not be such an edge case that it isn't supported.