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
I have some problems with converting values of the OrderedDictionary of the PowerShell to the string line. I have the following hash table [OrderedDictionary]:
I try outputting data of values to a string:
for ($i = 0; $i -lt $DataResult.Count; $i++) {
$DataResult["$i"].Values |
ForEach-Object {
Write-Output $_
}
}
But it doesn't work, could you help me
Given your code sample I think you have an array of [Ordered] dictionaries? If so you should be able to unroll the values quite easily:
#This is just demo data for my testing:
$Dictionaries = #(
[Ordered]#{
P1 = 'Something'
P2 = 'SomethingElse'
}
[Ordered]#{
P1 = 'Another'
P2 = 'AnotherAnother'
}
)
# Unroll the values:
$Values = $Dictionaries.Values
If you ignore the demo data it's really just 1 line. $Values would be of type [Object[]] the elements of which are in their original string type. If needed you can re-cast as a string array:
$Values = [String[]]$values
Or you can specify directly with the unrolling:
# Unroll the values:
$Values = [String[]]$Dictionaries.Values
Or Type constrain the variable:
# Unroll the values:
[String[]]$Values = $Dictionaries.Values
Note: This casting will convert element values to strings as well. I'm going on the basis that's desired.
I'd also point out you don't really need Write-Output anywhere. Firstly, that's already implicit in normal PowerShell operations. Secondly, you cannot pipe a traditional For loop (although you can assign its output to a variable). At any rate, if your intent is to simply continue feeding this down the pipeline, you could remove $Values from any of the above examples and PowerShell will implicitly and natively do just that.
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
# ...
}
I have a data structure for a multi-level lookup table that looks something like this
$lookupTable = #{
'a' = #{
'a1' = 'A One'
'a2' = 'A Two'
'a3' = 'A Three'
}
'b' = #{
'b1' = 'B One'
'b2' = 'B Two'
'b3' = 'B Three'
}
}
I use a data structure like this to pass an arbitrary number of new and revised values to update the table, like this.
$hash=#{'a'=#{'a4'='A Four'}
'b'=#{'b4'='B Four'}
'c'=#{'c1'='C One'
'c2'='C Two'
}
}
I can then use nested foreach loops to get the key names to add or update.
However, I also need to extract just a single value, and I want to keep a similar data structure so I can pass just one hash table to specify exactly what value I am looking for, such as $getValue = #{'b'='b1'}.
But to do that I need to get the key name at the first(only) index. I have tried
Write-Host "$($getValue.GetEnumerator[0])"
Write-Host "$($getValue.keys.GetEnumerator[0])"
Write-Host "$($getValue.GetEnumerator[1])"
Write-Host "$($getValue.keys.GetEnumerator[1])"
and none work. I could use a foreach again, which will only look once, but that seems inelegant. So, what am I doing wrong?
EDIT: Just to verify the loop based approach works, I did this
foreach ($key in $getValue.keys) {
if ($key) {
Write-Host "$key $($getValue.$key) = $($lookupTable.$key.($getValue.$key))"
}
break
}
and it does indeed come back with b b1 = B one. but what an ugly way to do it.
Dang, getting closer!
$key = $getValue.keys[0] does provide a key of b. But $value = $getValue.$key doesn't return b1 as expected. However, $value = $getValue[$key] does! however, then trying
$key = $getValue.keys[0]
$value = $getValue[$key]
Write-Host "$key $($getValue[$key]) = $($lookupTable.$key.$value)"
doesn't come back with the actual final value. Grr. More coffee and try again.
AHA! $getValue.keys[0] doesn't return a string. But if you cast to a string then everything is dandy. So
$key = [string]$getValue.keys[0]
$value = $getValue.$key
Write-Host "$key $value = $($lookupTable.$key.$value)"
works a treat. So I answered my own question. I guess I'll let the mods decide if this might be educational for others, or should just be deleted.
I think you can simplify this by declaring the $getValue not as a Hashtable, but rather as a simple array:
# define the value to get as array of keys.
# Element 0 => the key for $lookupTable,
# Element 1 => the key for the first nested hash
$getValue = 'b','b1'
# now, simply get the value you are looking for like this:
Write-Host $lookupTable[$getvalue[0]][$getValue[1]]
# or like so:
Write-Host $lookupTable.$($getvalue[0]).$($getvalue[1])
# or by using helper variables:
$lookupKey, $nestedKey = $getValue
Write-Host $lookupTable.$lookupKey.$nestedKey
All of the above will return the value B One
I have a variable called: $backendSubnet
This variable currently contains 4 entries, when I do $backendSubnet.Count it returns '4'
This number of entries will change each time the script is run. What I need to do it automatically break out (based on the number of entries) in this instance there's 4 - to be used in another CmdLet, see.....
-Subnet $backendSubnet1,$backendSubnet2,$backendSubnet3,$backendSubnet2
I need to automate breaking this out arranged like this above.
I have tried the following, bit I think I am on the wrong track:
$max = $backendSubnet.Count -1;0..$max | % {$backendSubnetArray += $backendSubnet[$_]}
I would suggest initializing a new array to a predefined size, then copy N elements from the $backendSubnet into the new array. Then rinse and repeat.
for ($i = 0; $i -lt $backendSubnet.Count; $i += $numToCopy)
{
$numToCopy = [Math]::Min($backendSubnet.Count - $i, $max)
$subset = new-object object[] $numToCopy
[Array]::Copy($backendSubnet, $i, $subset, 0, $numToCopy)
SomeCmdlet -Subnet $subset
}