Trying to edit single elements in dynamic jagged array edits several instead - arrays

I am trying to make a dynamically sized jagged array to represent a game grid for a very simple console game.
# Determine the size of the board. Enforce the size so as to not make it too large.
$boardSize = 5
# Game board characters
[char]$bushCharacter = "#"
# Initialize Game board object
$gameBoard = ,(#($bushCharacter) * $boardSize) * $boardSize
Idea being that if I was to print it out on the screen. I would get something like this which I do.
0..($boardSize - 1) | ForEach-Object{$gameBoard[$_] -join ""}
#####
#####
#####
#####
#####
All is well until I try to make a change to an individual item: $gameBoard[0][1] = "F". My expected result from reprinting the grid is
#F###
#####
#####
#####
#####
My actual output is:
#F###
#F###
#F###
#F###
#F###
This tells me that the array I made has all rows referencing each other. Why would this code not create separate unique arrays? What change could I make to make it work. I know other approaches would work like a index loop with += but I thought this approach was a better practice.

If you look at the documentation for the multiplication operator (*), you'll find that (from about_Arithmetic_Operators):
* Multiplies integers; copies strings 6*2
and arrays the specified number of "w" * 3
times.
Notice the word copies.
Now take a look at the MSDN documentation for the .NET Array.Copy() method, under the Remarks section:
If sourceArray and destinationArray are both reference-type arrays or are both arrays of type Object, a shallow copy is performed. A shallow copy of an Array is a new Array containing references to the same elements as the original Array. The elements themselves or anything referenced by the elements are not copied. In contrast, a deep copy of an Array copies the elements and everything directly or indirectly referenced by the elements.
It's fair to assume that the array multiplication operator always produces the same kind of array - ie. either a reference-type array or an Object array (whatever that is) - and this is basically what you see the effects of.
I'd do:
$gameBoard = 1..$boardSize |ForEach-Object { ,#(#($bushCharacter) * $boardSize) }

Array is a reference type and because of that you are only multiplying (copying) the reference/pointer and not the content itself.
Workaround: Create a loop to generate unique arrays.
$gameBoard = 0..($boardSize - 1) | ForEach-Object{ ,(#($bushCharacter) * $boardSize) }
You can verify (without documentation) that an object is a reference type by using:
Add-Type -AssemblyName "Microsoft.VisualBasic"
[Microsoft.VisualBasic.Information]::IsReference($gameBoard[0])
True
#or as mentioned by Mathias
-not $gameBoard[0].GetType().IsValueType
True

Related

Cannot add single element array to hashtable in powershell [duplicate]

I have a powershell 5.1 script that I've created that imports a csv file, manipulates some of the data, then converts the object of data to json format using the ConvertTo-Json cmdlet at the end of the script. The problem that I'm running into; in one of my fields I need to create a single array for the object property with brackets. So I need the object to be like:
"PersonGroups":[
{
"Name":"test Name",
"Id": 3433
}
]
Here is the call to the function:
$_.PersonGroups = Set-DataHash -InputObject $_
Below is my code that I have:
function Set-DataHash{
param(
[psobject] $InputObject
)
$customObject = [psobject]#{
Name = "Test"
Id = 78888
}
$customArray = #($customObject)
return $customArray
}
Of course if I have more than one object in the array it works fine; but since it's only one object ConvertTo-Json makes it a single object. Any suggestions on what to do?
How about:
$_.PersonGroups = #(Set-DataHash -InputObject $_)
You don't need the "return".
js2010's helpful answer shows how to solve the problem on the caller's side, using #(), the array-subexpression operator, around your Set-DataHash function call to ensure that the return value is an array.
If you want to solve the problem from within your function, you must ensure that the array being output (returned) is output as a whole, as a single object, which can be achieved with an auxiliary single-element wrapper array created with (the unary form of) ,, the array constructor operator:
function Set-DataHash {
param(
[psobject] $InputObject
)
$customObject = [psobject] #{
Name = "Test"
Id = 78888
}
# Wrap $customObject in an array with #(...),
# then *additionally* wrap this array in an *auxiliary* single-element array,
# via `,` (array-construction operator).
# Outputting this aux. array causes *it* to be enumerated, which therefore
# outputs its only element as-is: the *original* array - see explanation below.
# Note: I've omitted `return`, which is always optional.
, #($customObject)
}
Explanation:
By default - irrespective of whether you use return or implicit output in a function or script - outputting a collection (including arrays) causes its elements to be enumerated (aka streamed, unwrapped or unrolled); that is, the elements are output one by one to the (invisible, in this case) pipeline through which the caller receives the output.
In the case of a single-element collection, the nature of the pipeline is such that the caller receives just that single element itself - the collection wrapper is lost.
In the case of a multi-element collection, the specific original collection is lost too, and the enumerated elements are automatically collected in an [object[]] array).
Therefore, wrapping a collection that you want to output as a whole, as a single object requires the aux. single-element wrapper array technique shown above; a less efficient alternative is to use Write-Output with the -NoEnumerate switch, which too prevents enumeration of a collection passed to it as an argument (not via the pipeline).
Note:
In general, in functions/scripts intended to be called directly by others, it is better not to output collections as a whole, so as not to confound the general expectation of streaming (enumerating) behavior in the pipeline; in the streaming case, it is the caller that must ensure that the collected output is an array, via #(...), as shown in js2010's answer; see this answer for background information.
Conversely, however, outputting collections as a whole is faster and allows you to output a specific collection type.

Create a Single-Element Json Array Object Using PowerShell

I have a powershell 5.1 script that I've created that imports a csv file, manipulates some of the data, then converts the object of data to json format using the ConvertTo-Json cmdlet at the end of the script. The problem that I'm running into; in one of my fields I need to create a single array for the object property with brackets. So I need the object to be like:
"PersonGroups":[
{
"Name":"test Name",
"Id": 3433
}
]
Here is the call to the function:
$_.PersonGroups = Set-DataHash -InputObject $_
Below is my code that I have:
function Set-DataHash{
param(
[psobject] $InputObject
)
$customObject = [psobject]#{
Name = "Test"
Id = 78888
}
$customArray = #($customObject)
return $customArray
}
Of course if I have more than one object in the array it works fine; but since it's only one object ConvertTo-Json makes it a single object. Any suggestions on what to do?
How about:
$_.PersonGroups = #(Set-DataHash -InputObject $_)
You don't need the "return".
js2010's helpful answer shows how to solve the problem on the caller's side, using #(), the array-subexpression operator, around your Set-DataHash function call to ensure that the return value is an array.
If you want to solve the problem from within your function, you must ensure that the array being output (returned) is output as a whole, as a single object, which can be achieved with an auxiliary single-element wrapper array created with (the unary form of) ,, the array constructor operator:
function Set-DataHash {
param(
[psobject] $InputObject
)
$customObject = [psobject] #{
Name = "Test"
Id = 78888
}
# Wrap $customObject in an array with #(...),
# then *additionally* wrap this array in an *auxiliary* single-element array,
# via `,` (array-construction operator).
# Outputting this aux. array causes *it* to be enumerated, which therefore
# outputs its only element as-is: the *original* array - see explanation below.
# Note: I've omitted `return`, which is always optional.
, #($customObject)
}
Explanation:
By default - irrespective of whether you use return or implicit output in a function or script - outputting a collection (including arrays) causes its elements to be enumerated (aka streamed, unwrapped or unrolled); that is, the elements are output one by one to the (invisible, in this case) pipeline through which the caller receives the output.
In the case of a single-element collection, the nature of the pipeline is such that the caller receives just that single element itself - the collection wrapper is lost.
In the case of a multi-element collection, the specific original collection is lost too, and the enumerated elements are automatically collected in an [object[]] array).
Therefore, wrapping a collection that you want to output as a whole, as a single object requires the aux. single-element wrapper array technique shown above; a less efficient alternative is to use Write-Output with the -NoEnumerate switch, which too prevents enumeration of a collection passed to it as an argument (not via the pipeline).
Note:
In general, in functions/scripts intended to be called directly by others, it is better not to output collections as a whole, so as not to confound the general expectation of streaming (enumerating) behavior in the pipeline; in the streaming case, it is the caller that must ensure that the collected output is an array, via #(...), as shown in js2010's answer; see this answer for background information.
Conversely, however, outputting collections as a whole is faster and allows you to output a specific collection type.

replace page of array with another using paste() R

I have a 3d array with lots of pages (or whatever you call 3rd dimension). I am trying to stick that array into a loop, and loop through the pages to fill in values.
In my real dataset, I'm manipulating values with a handful of custom functions, so the way it works best to minimize the amount of retyping I would need to do when changing variables is to create a new array inside the loop, make changes to that new array, then replace the new page in its spot in the original array. However, I'm having a hard time plugging the new page back into the original array. Example follows.
code="A" # I'll be running this loop with various name codes
assign(paste0("testarray",code),array(dim=c(5,5,20)))
# creates array "testarrayA" . I'm using assign(paste()) because I will
# do this with many different array codes, and I want to only change
# the code value each time for ease
set.seed(5)
testarrayA[,,1] <- runif(25,3,20) #fill first page
Then I want to run this array through a loop to change the values on each page. In the real code, the changes are made with somewhat complex custom functions, so again, for ease, I start by making a smaller array, changing that, then trying to plug back into the array from above. See following.
# this loop doesn't work at the moment, so just run lines
# inside the loop individually to attempt. set "page" object as 1
for(page in 1:dim(get(paste0("testarray",code)))[3] ){
# for pages 1-end of third dimension (20)
temparray <- get(paste0("testarray",code))[,,page:(page+1)]
# create smaller array of only current and (blank) next page of original array
temparray[,,2] <- temparray[,,1]*2
# do functions to change the values on the next page of temporary array.
# (just multiplying values by 2 for this example)
# try to plug in temporary array[2] as original array[page+1].
# both of these codes give me errors at the moment, but these are
# the options I have tried so far.
# trial 1:
get(paste0("testarray",code))[,,page+1] <- temparray[,,2]
# trial 2:
assign(paste0("testarray",code)[,,page+1],temparray[,,2])
}
I believe the problem comes from not being able to use paste() in the receiving end of an assign command. The first trial listed above returns error "target of assignment expands to non-language object," which I'm not quite sure how to interpret, and the second, "incorrect number of dimensions," which shouldn't be the case. When I highlight and run each object individually, they have the same dimensions, so I don't know what I'm doing wrong.
Basically, I need to know how to use paste() on the receiving end of an assign function such as <- or assign(), or if there's a reasonable workaround for the errors I'm getting. I know I could just omit the creation of the smaller temporary array, but in my actual code, that would make a lot more work by requiring me to change names a bunch of times in each command inside the loop when I run on a new dataset. I'd rather just change the "code" if possible. Thanks!

In order to preallocate memory in Matlab, I want to initialize my array of objects. How do I do this?

I have a class of objects known as blocks. Currently, I am creating an array of blocks using a for loop by simply tacking them unto an empty array
blockArray=[];
for ii=1:Size
blockArray=[blockArray block(....)];
end
In order to preallocate memory, how do I initialize an object array of blocks with dummy values?
For instance if instead of using block objects I used numbers, I could easily preallocate by using zeros(1,Size). Is there something similar that I could do?
The matlab documentation describes
To preallocate the object array, assign the last element of the array first. MATLABĀ® fills the first to penultimate array elements with default DocArrayExample objects.
So, to do this, instead of iterating over from 1:size, it is simpler to do...
blockArray = []
blockArray(size) = block(...)
The language does not really support this, there exists multiple solutions (or workarounds).
Replicating the first instance
When pushing the first element into the array, you can fill the whole array with this element to achieve a preallocation. This might look very bad, but it is actually the fastest possibility known to me.
for ii=1:S
%assumption e is a scalar, otherwise the code is totally screwed
e=block(....)
if ii==1
%to do preallocation, fill it with your first element
blockArray(1:S)=e
else
blockArray(ii)=e
end
end
Use cell arrays
Obvious simple solution, you can put any class into the fields
blockArray=cell(S,1);
for ii=1:S
%assumption e is a scalar, otherwise the code is totally screwed
e=block(....)
blockArray{ii}=e
end
This solution is very simple but slower than the first. You also lose some functionality which is not available for cell arras
Let your class implement array functionality
classdef A
properties
arg1
out
end
methods
function obj = A(varargin)
if nargin==0
%null object constructor
elseif strcmpi(varargin{1},'createarray')
sz=varargin(2:end);
%preallocate
obj(sz{:})=A;
else
%standard constructor
obj.arg1=varargin{1};
obj.out=[1 2 3;];
end
end
end
end
Constructor with no input argument creates an "empty" or "null" object, this is used to preallocate so it should be empty.
Constructor with first parameter makearray creates an array
Otherwise your constructor should be called.
Usage:
%create an empty object
A
%create an array of size 2,3,4
A('createarray',2,3,4)
%use the standard constructor
A(2)
Biggest downside is you have to modify your classes. Never tested this solution, but it should be close to the first in performance.

What does this specific value hold? Perl

my #array = #{$array_ref};
I'm not entirely sure what this array is storing. Can anyone care to explain to me what is happening? Thanks.
$array_ref presumably holds a reference to an array, which is different from an array itself. An array (named #some_array) has elements (unless it's empty). An array ref (named $some_arrayref) is a scalar value that is used to reference an actual array. The actual array may be a named array (as in, it has a variable name), or may be an anonymous array (as in, no named variable, but referred to exclusively by reference).
To use the elements held in the array referred to by $array_ref, you have to dereference it. One syntax is #$array_ref (read that as "The array (#) referred to by the scalar ($) named array_ref). I've never been a fan of jamming a bunch of sigils together (the #, $ and % symbols) when dereferencing. I feel it hampers readability of the code, particularly as the references become more complex or nested. Perl allows for curly brackets, which help to disambiguate and visually clarify what's going on. #{$array_ref} can be read as dereference as an array ( #{....} ) the array referred to by $array_ref.
From Perl's POD, have a look at perlreftut. It's a great starting point.
The code makes a top-level copy of the array referenced by $array_ref and stores it in #array.
To make a deep-level copy, use dclone from Storable:
use Storable;
#array = #{ dclone( $array_ref ) };
See perldoc Storable for details.
Storable is a standard module that is install with Perl. To see a list of all standard pargmatics and modules, see perldoc perlmodlib.
This is simply dereferencing the array ref. $array_ref contains a reference to an array, #array is set to the contents of the referenced array.
$ perl -E 'my $a = [1, 2, 3]; my #b = #{$a}; say $a; say #b'
ARRAY(0x100803ea0) # a reference to an array
123 # actual array
You need to read perlref.
In short, #array is the same array that $array_ref was pointing to. That is the most precise answer that I can give without knowing what got put into $array_ref.
$array_ref is a reference to a list / array.
so in order to dereference it in a list content and get all the contents, you use
#{$array_ref}
and this can be assigned into a different array / list like:
my #array = #{$array_ref};

Resources