How to stop Powershell from flattening parameter of jagged array? - powershell

I created a hashtable representing the structure of the file I'm extracting data from
# schema of the data stored in the file
$RecordSchema = #{
Header = #(
#("EmployerName", 2, 30)
#("ApplicableMonth", 32, 6)
#("EmployerIDNumber", 38, 10)
)
}
# function to extract data from the file based on the $schema
function Map-Field ($row, $schema) {
$mappedRow = #{}
foreach ($field in $schema) {
$fieldName = $field[0]
$fieldPosition = $field[1]
$fieldSize = $field[2]
$value = $row.Substring($fieldPosition, $fieldSize)
$mappedRow[$fieldName] = $value
}
[PSCustomObject]$mappedRow
}
function Set-RecordHeader($record, $recordRaw) {
$record["Header"] = Map-Field $recordRaw[0] $RecordSchema["Header"]
$record
}
When I run the script, $schema gets the flattened version of the schema $RecordSchema.Header I've passed.
I've added comma before the parameter $RecordSchema["Header"] yet I got an array of single-item array and that item contains the flattened version of the schema I'm passing.
$record["Header"] = Map-Field $recordRaw[0] (,$RecordSchema["Header"])

I've just discovered that for some reason, I need to add a comma at the end of each array
$RecordSchema = #{
Header = #(
#("EmployerName", 2, 30), # this
#("ApplicableMonth", 32, 6), # and other comma matter
#("EmployerIDNumber", 38, 10)
)
}
I verified it by running the following
$a = #(
#(1, 2, 3)
#(4, 5, 6)
)
$b = #(
#(1, 2, 3),
#(4, 5, 6)
)
$a.Length # returns 6
$b.Length # returns 2
I've thought PowerShell will think that passing a new line means another entry :-(

Related

Missing property name after reference operator

I have a map, that is originally c++ code, file read, and parsed into a map. The original code was an enum, and didn't have values for all items. $fileContent:
enum{
Error_A = 110,
Error_B,
Error_C,
Error_D,
Error_E,
Error_F,
Error_G = 118,
...
};
I have read the file contents and put it in a map like this (works fine):
function Get-Contents_b{
[cmdletbinding()]
Param ([string]$fileContent)
#Error_AA = 20
# create an ordered hashtable to store the results
$errorMap = [ordered]#{}
# process the lines one-by-one
switch -Regex ($fileContent -split '\r?\n') {
'^[\s]*([\w]+)[\s=]*([-\d]*)' { # Error...=12345
$key,$value = ($matches[1,2])|ForEach-Object Trim
$errorMap[$key] = $value
}
}
...
Then I want to iterate over the map, and for the ones with enum values dependent on single digit increase from the previous, I want to assign the value of the previous value plus one. I'm trying to do that below, but getting the $previousKey, using $key-1, and then getting the value from that, is giving the error shown in the comment.
foreach ($key in $errorMap.$keys)
{
$previousKey = $errorMap.[($key-1)] #missing property name after the reference operator
Write-Host $errorMap.$previousKey
if(($errorMap.$key).Value = "")
{
$errorMap.$key.Value = $errorMap.$previousKey.Value + 1
}
}
Any ideas how to fix this or get the previous value and assign the next empty value the previous value plus one?
This is with powershell 5.1 and VSCode.
You're mistakenly mixing member (property) access via the . operator with indexed access via [...] - you must use one or the other.
However, what you want is positional access to your keys (which only works with an ordered hashtable):
foreach ($keyIndex in 0..($errorMap.Count-1))
{
if ('' -eq $errorMap[$keyIndex]) {
$previousValue = $errorMap[$keyIndex - 1]
Write-Host $previousValue
$errorMap[$keyIndex] = 1 + $previousValue
}
}
Why not create the values in your Hashtable straight away instead of filling the empties afterwards?
function Get-Contents_b{
[cmdletbinding()]
Param ([string]$fileContent)
# create an ordered hashtable to store the results
$errorMap = [ordered]#{}
$currentValue = 0
# process the lines one-by-one
switch -Regex ($fileContent -split '\r?\n') {
'^\s+(\w+)\s*[,=]'{
$key, $value = ($_ -split '[,=]', 2).Trim()
if ([string]::IsNullOrWhiteSpace($value)) { $value = $currentValue }
$errorMap[$key] = [int]$value
$currentValue = [int]$value + 1
}
}
# return the map
$errorMap
}
Get-Contents_b $enum
Output:
Name Value
---- -----
Error_A 110
Error_B 111
Error_C 112
Error_D 113
Error_E 114
Error_F 115
Error_G 118

Processing items within a powershell array - multiple items

I have been reading about arrays in powershell and hash tables, I know the basic workings of an array and how to use foreach loop to get items within the array, my challange here is slightly different. I would like to pay what I call a multi dimension array to a script, and process the items contained within the array.
What is my setup.
$x = (1,"Server1",3,1),(4,"Server2",6,2),(3,"Server3",4,3)
$k = 'serverid','servername','locationid','appid' # key names correspond to data positions in each array in $x
$h = #{}
For($i=0;$i -lt $x[0].length; $i++){
$x |
ForEach-Object{
[array]$h.($k[$i]) += [string]$_[$i]
}
}
What am i trying to achieve ?
I am trying to achieve the structure of a database table within powershell. So literally treating each array item as a row.
So for example
(1,"Server1",3,1),(4,"Server2",6,2),(3,"Server3",4,3)
could be thought of as a table like below
enter image description here
I then want to loop for each item in the array to get the values, similar to the below example
[0].serverid = 1, [0].servername = server1, [0].locationid = 3, [0].applicationID = 1
[1].serverid = 4, [1].servername = server2, [1].locationid = 6, [1].applicationID = 2
what have I done ?
$x = (1,"Server1",3,1),(4,"Server2",6,2),(3,"Server3",4,3)
$k = 'serverid','servername','locationid','appid' # key names correspond to data positions in each array in $x
$h = #{}
For($i=0;$i -lt $x[0].length; $i++){
$x |
ForEach-Object{
[array]$h.($k[$i]) += [string]$_[$i]
}
}
$x
for ($i = 0; $i -lt $x.Count; $i++)
{
$myserverid = $x[$i][0]
$myservername = $x[$i][1]
$mylocationid = $x[$i][2]
$myappid = $x[$i][3]
write-host $myserverid
}
The Issues
If I set the following $x = (1,"Server1",3,1), then the loop is somewhat incorrect which is why I think the approach is wrong (more than one item works i.e $x = (1,"Server1",3,1),(4,"Server2",6,2),(3,"Server3",4,3)). The loop only works if you have more than one item within the array, hence why I want to re-examine the way the loop works.
Thanks in advance
Your approach relies on a nested (jagged) array: That is, you have an array of subarrays, each of which represents the tuple of values you want to assign to properties.
If there's only one subarray, you must create the nested array explicitly, using the unary form of , the array constructor operator:
# Construct a 1-element array that contains the 4-element subarray.
$x = , (1,"Server1",3,1)
With two or more, subarrays, you implicitly get a nested array:
# Construct a 3-element array, each element of which contains a 4-element subarray.
$x = (1,"Server1",3,1), (4,"Server2",6,2), (3,"Server3",4,3)
Note that in PSv5+ you could use a custom class to solve your problem:
class Custom {
[int] $serverid; [string] $servername;[int] $locationid; [int] $appid
Custom($propValueArray) {
$this.serverid = $propValueArray[0]; $this.servername = $propValueArray[1]; $this.locationid = $propValueArray[2]; $this.appid = $propValueArray[3]
}
}
# Use an array cast to construct [Custom] instances.
# Note the need for (...) around the array, because casts have high
# precedence in PowerShell.
[Custom[]] ((1,"Server1",3,1), (4,"Server2",6,2), (3,"Server3",4,3))
This would allow for processing such as:
# Construct all objects
$objects = [Custom[]] ((1,"Server1",3,1), (4,"Server2",6,2), (3,"Server3",4,3))
# Process each object.
foreach ($object in $objects) {
($myserverid = $object.serverid) # assign a property to a var; `()` also outputs
# ...
}

PowerShell - why does looking up an array in a Hashtable return an empty array?

I hit this while looking for something else, so there is no wider code or purpose beyond puzzling over this example:
$H = #{} # new, empty hashtable
# String test - lookup fails, hashtable returns $null
$H["test"].GetType() # "You cannot call a method on a null-valued expression."
# Array test - should do the same
$Key = #('a','b')
$H[$Key].GetType() # Object[] what?? Where did this come from? <--<<====<<====<<
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Double check that ..
$H.ContainsKey($Key) # False. Doesn't contain the key(!)
$H.Values.Count # 0. It's empty. As it should be.
How (why) does a lookup on an empty hashtable return an object[] when the key is an array, but not otherwise?
NB. I'm aware that you can't/shouldn't use an array as a hashtable key at least partly because arrays are not equal unless they are the same object in memory e.g. array/object keys for hashtables in powershell; but arrays do have .GetHashCode() - shouldn't it still return $null for a failed lookup no matter what they key is?
Happens on Windows/PSv4 and Linux/PS6-alpha
Here is the CoreCLR Hashtable source for this[Object key] although I can't make anything relevant of that. Maybe this is in the higher level PowerShell hashtable handling (which I haven't found yet).
This is PowerShell feature which allows you to specify array of indexes when indexing a collection:
$Array = 10..1
$Indexes = 1, 3, 5
$Array[$Indexes] # 9, 7, 5
$Hash = #{
a = 1
b = 2
c = 3
}
$Indexes = 'c', 'f', 'a'
$Hash[$Indexes] # 3, $null, 1
# System.Collections.Hashtable designed to return null,
# when asking not existing key.
$String = 'abcdefghijklmnopqrstuvwxyz'
$Indexes = 7, 4, 11, 11, 14
$String[$Indexes] # h, e, l, l, o
If you really want to index hashtable by array as single object, then you can use Item parameterized property:
$Array = 1..10
$Hash = #{ $Array = 'Value' }
$Hash.Item($Array)

Out do I concatenate the beginning of an array subscript of a substring and the end substring

$getRead = #($readFile)
for($i = 0; $i -lt getRead.length; $i++){
$getEnd = $readFile.substring(readFile[$i].length - 1, 4)
$getStart = $readFile.substring(readFile[$i].length 1, $getEnd)
#$findMatchREGEX = "[0-8]"
#$replaceWithREGEX = "9"
#$newNum = $readFile -replace $findMatchREGEX,$replaceWithREGEX
}
I am reading in drivers license numbers of varying lengths for all 50 states. I need to change the last 4 digits to 9
How do I get the beginning portion of the subscript in an array and then concatenate them together?
This will work provided you have a single driver's license number, per line in you file:
$readFile = "c:/path/file"
$getRead = gc $readFile
foreach ($line in $getRead){
$getStart = $line.substring(0, $line.Length - 5)
$resultWithNines = $getStart + "9999"
echo $resultWithNines
}

Error with accessing Array name in perl

I am doing a project in perl to search each elements from an array with all the other element and print the element and the array name in which the match occours.
Since the code is very long I'm explaining my problem with short example.
#array1=(sdasd,asdasd,abc);
if(abc=~/$array1[2]/)
{
print"Found!";
}
else
{
print"not found!"
}
When i try searching for the pattern with the above method I get the answer. As there are many arrays and each array contain many elements i gave array name as #array1 , #array2 ... so that i can search using loops .
so i tried this method
#array1=(sdasd,asdasd,abc);
$arrayno = 1;
if(abc=~$array$arrayno[2])
{
print"Found!";
}
else
{
print"not found!"
}
I'm getting the following error
(Missing operator before $no?)
syntax error at C:\Perl64\prac\pp.pl line 4, near "$arra$no"
Execution of C:\Perl64\prac\pp.pl aborted due to compilation errors.
It would be much simpler to hold all of your arrays in the same structure, you can place as many arrayrefs as you like inside of an array and iterate over them in a nested foreach loop:
my #arrays = ( [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
);
my $array_counter = 1;
foreach my $aref ( #arrays ) {
foreach my $elem ( #$aref ) { # place arrayref in list context
# do comparison here
if ( # MATCH ) { print "Found match in Array $array_counter\n"; }
}
$array_counter++; # increment counter
}