I can't understand this behavior in powershell :
$PSVersionTable.PSVersion
""
$matrix =, @(, @("foo") * 5) * 3
$matrix[0][1] = "bar"
foreach($subArray in $matrix){
Write-Host $($subArray[1])
}
With this script I'm initializing an array of 3 arrays, each containing 5 string "foo".
I want to set the 2nd element of the 1st array to "bar", and each arrays are affected :
# Returned :
Major Minor Build Revision
----- ----- ----- --------
5 1 15063 1155
bar
bar
bar
Is it a bug or am I missuntderstanding how arrays works ?
What the *
operator de facto does with an array as the LHS is to use that array's elements as-is as the elements of the resulting array.
In the case at hand, this means that $matrix[0]
, $matrix[1]
, and $matrix[2]
all point to the same 5-element array composed of "foo"
strings, so modifying that one array's elements will be reflected in all 3 $matrix
elements.
This behavior is likely by design (see bottom section), and I am unaware of a concise workaround with the *
syntax.
A viable workaround is:
# Note: The (...) around the arrays to replicate aren't strictly needed,
# because `,` has higher precedence than `*`, but were added for
# readability.
$nestedArrayTemplate = (, 'foo') * 5
$matrix = (, @()) * 3 # (, $null) * 3 would work too
for ($i=0; $i -lt $matrix.Count; ++$i) { $matrix[$i] = $nestedArrayTemplate.Clone() }
As an aside: the above creates a jagged array, not a matrix (2-dimensional array). Here's how you would create a true 2-dimensional array:
$matrix = [string[,]]::new(3, 5) # PSv4-: New-Object string[] 3, 5
for ($row = 0; $row -lt $matrix.GetLength(0); ++$row) {
for ($col = 0; $col -lt $matrix.GetLength(1); ++$col ){
$matrix[$row, $col] = 'foo'
}
}
Note that indexing into this 2-dimensional array then requires a single index expression with a list of indices; e.g., [0, 1]
rather than [0][1]
.
Design musings:
I can only speculate about the design intent, but I presume that no attempt is made to clone collections that happen to be the elements of the LHS array, because there's no guarantee that such collections can be cloned - the same more generally applies to all element types of the LHS array that are reference types (other than strings).
With LHS array elements that are value types or strings - which is the more typical case - there's no problem, because the resulting array's elements will (effectively) be independent copies.
In short: for predictable array replication with *
, restrict the LHS array's element types to value types or strings.