Iterate over elements in hash and updating its values - powershell

At the beginning I set all hash values to null.
When a name in the hash is equal to a certain name the hash value should be set to 1.
However, I get an error
collection was modified, enumeration may not execute
Which is really stupid, since I do not change the values for the keys. Any ideas?
$Fieldlist = #{
"Brand Name" = 0;
"Product Type" = 0
}
# Loops thru an array
$ArrayOfNames = ("Name1", "Name2", ... ,"Brand Name" ...)
foreach ($FieldName in $Fieldlist.Keys) {
$Message = $FieldName + " -eq " + $ColName + " ?"
Write-Host $Message
if ($FieldName -eq $ColName) {
Write-Host "-------> Yes, bingo"
$Fieldlist.$FieldName = 1
}
}

Reverse your logic. Instead of iterating over the keys of the hashtable and comparing them to a value from $ArrayOfNames (I suppose that's where $ColName is coming from) iterate over the name array and check if the hashtable contains that key before changing the value:
foreach ($FieldName in $ArrayOfNames) {
if ($Fieldlist.ContainsKey($FieldName)) {
$Fieldlist.$FieldName = 1
}
}

Related

A dictionary inside of a dictionary in PowerShell

So, I'm rather new to PowerShell and just can't figure out how to use the arrays/lists/hashtables. I basically want to do the following portrayed by Python:
entries = {
'one' : {
'id': '1',
'text': 'ok'
},
'two' : {
'id': '2',
'text': 'no'
}
}
for entry in entries:
print(entries[entry]['id'])
Output:
1
2
But how does this work in PowerShell? I've tried the following:
$entries = #{
one = #{
id = "1";
text = "ok"
};
two = #{
id = "2";
text = "no"
}
}
And now I can't figure out how to access the information.
foreach ($entry in $entries) {
Write-Host $entries[$entry]['id']
}
=> Error
PowerShell prevents implicit iteration over dictionaries to avoid accidental "unrolling".
You can work around this and loop through the contained key-value pairs by calling GetEnumerator() explicitly:
foreach($kvp in $entries.GetEnumerator()){
Write-Host $kvp.Value['id']
}
For something closer to the python example, you can also extract the key values and iterate over those:
foreach($key in $entries.get_Keys()){
Write-Host $entries[$key]['id']
}
Note: You'll find that iterating over $entries.Keys works too, but I strongly recommend never using that, because PowerShell resolves dictionary keys via property access, so you'll get unexpected behavior if the dictionary contains an entry with the key "Keys":
$entries = #{
Keys = 'a','b'
a = 'discoverable'
b = 'also discoverable'
c = 'you will never find me'
}
foreach($key in $entries.Keys){ # suddenly resolves to just `'a', 'b'`
Write-Host $entries[$key]
}
You'll see only the output:
discoverable
also discoverable
Not the Keys or c entries
To complement Mathias R. Jessen's helpful answer with a more concise alternative that takes advantage of member-access enumeration:
# Implicitly loops over all entry values and from each
# gets the 'Id' entry value from the nested hashtable.
$entries.Values.Id # -> 2, 1
Note: As with .Keys vs. .get_Keys(), you may choose to routinely use .get_Values() instead of .Values to avoid problems with keys literally named Values.

powershell convert-tojson not giving optimal results

I have a powershell question, I need to convert this piece of code to Json
$Dimensions = #(
#{
name = 'num_vm'
value = '100'
},
#{
name = 'days'
value = '10'
},
#{
name = 'months'
value = '12'
}
)
For ($i=0;$i -lt $Dimensions.length;$i++)
{
$bodymessage = #{'resourceId' = $resourceUsageId; 'quantity' = "$Dimensions[$i].value"; 'dimension' = "$Dimensions[$i].name"; 'effectiveStartTime' = $lastHourMinusFiveMinutes; 'planId' = 'plan_meter' }
$body += #{
request =
#($bodymessage)} `
|ConvertTo-Json -Depth 5
}
$body
what I'm hoping, is it can dynamically create the JSON output according to the dimension input,
but the result I got is this
{
"request": [
{
"resourceId": null,
"quantity": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[0].value",
"planId": "plan_meter",
"dimension": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[0].name",
"effectiveStartTime": null
}
]
}{
"request": [
{
"resourceId": null,
"quantity": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[1].value",
"planId": "plan_meter",
"dimension": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[1].name",
"effectiveStartTime": null
}
]
}{
"request": [
{
"resourceId": null,
"quantity": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[2].value",
"planId": "plan_meter",
"dimension": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[2].name",
"effectiveStartTime": null
}
]
}
how can i get rid of System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[0].value?
and displaying with real value?
The main issue is how you're assigning the hashtable element values within a string. Fix it by removing the quotes like so (formatted multiline for readability):
$bodymessage = #{
resourceId = $resourceUsageId;
quantity = $Dimensions[$i].value; # Removed the quotes here
dimension = $Dimensions[$i].name; # Removed the quotes here
effectiveStartTime = $lastHourMinusFiveMinutes;
planId = 'plan_meter';
}
Note: I've also removed the single-quotes ' around the key names since they are not needed in this context. You only need to quote key names if non-alphanumeric characters are part of the key name, and you are assigning the key name as a literal string.
Why did this happen?
This happens because of how variable expansion within strings works. The following string you originally had:
"$Dimensions[$i].value"
will see $Dimensions get expanded (its ToString() function is implicitly called), not $Dimensions[$i].value. This is because variable parsing starts at the dollar-sign $ and stops at the first character which can't normally be part of a variable name. This will be most special characters, excepting the colon :.
However, if you are trying to force a string value (and you may not be), you can perform a sub-expression within the string to evaluate the members of an object, including array-accessors and object properties. You can also perform more complex expressions as well, but for your case below you could leave the quotes and use $() to get the result you want:
"$($Dimensions[$i].value)"
The $() tells the parser to "run this expression and put the result into the string". Again, whatever object is returned will implicitly have ToString() run on it when it gets inserted into the final string, so whatever output $Dimensions[$i].value.ToString() produces will be your string value.

Retrieve value from Json object with field having dots and hyphen in powershell

I want to retrieve value of a field present in json object. The filed name has dots and hyphen.
For eg:
$json = #"
{
"Stuffs":
{
"Name.new-name": "Darts",
"Type": "Fun Stuff"
}
}
"#
How can I get the value Darts?
I tried some approaches like
$x = $json | ConvertFrom-Json
$x.Stuffs.(Name.new-name)
But it doesn't work.
Try this
$x.Stuffs.'Name.new-name'

Populating a HoH, whose outer hash keys are in one array, and inner hash values are in another

Explanation
One array holds a set of keys. The values to these keys are inner hashes. The keys of these inner hashes, in this case are numbers (like array indices). Another array holds the values of the inner hash.
Question:
How can you populate the outer hash keys with the correct corresponding values (ie. correct inner hash)?
Contraints
I'd prefer a solution utilizing slices, map or grep. Eliminating cascading for loops
I realize it should be an HoA. But this is only for me to learn, it has no functional value...
Working Code:
This code works as I want but I would like to use more advanced techniques:
#! usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %register=();
my #classNames = ('Science_class', 'Math_class');
my #Science_class_student_names = ('George', 'Lisa', 'Mathias'); #prob from file
my #Math_class_student_names = ('Martin', 'Anna', 'Peter', 'George'); #prob from file
foreach my $className (#classNames) {
my $array_name = $className.'_'.'student_names';
if ($array_name =~ /Science/) {
foreach (0..$#Science_class_student_names ) {
$register{$className}{$_ + 1} = $Science_class_student_names[$_];
}
}
elsif ($array_name =~ /Math/) {
foreach (0..$#Math_class_student_names ) {
$register{$className}{$_ + 1} = $Math_class_student_names[$_];
}
}
}
print Dumper(\%register);
Ideas
A hash slice works for direct key-value pairs, but the intermediate keys are throwing me off. Trying something like: #register{#classNames} = map{$count => $student}
One idea I had, before the if statements was if there was a way to use a string in the name of an array: $#($array_name)student_names but that doesn't work.
Another would be to separately create an array of all the inner hash keys, use a slice and then put that hash into the outer hash.
The only other idea I had was using an AoA to hold all the 'inner hash value' arrays. (ie. my #studentNames = (\#Science_class_student_names, \#Math_class_student_names); but haven't gotten anywhere with that yet.
It's easier to work on it a layer at a time. For the inner layer, it seems you want
1 => George,
2 => Lisa,
...
So start by figuring out how to do that.
map { $_+1 => $Science_class_student_names[$_] }
0..$#Science_class_student_names
So you end up with
$register{'Science_class'} = {
map { $_+1 => $Science_class_student_names[$_] }
0..$#Science_class_student_names
}
};
$register{'Math_class'} = {
map { $_+1 => $Math_class_student_names[$_] }
0..$#Math_class_student_names
}
};
If you generalise the inner layer, you get
for (
[ 'Science_class' => \#Science_class_student_names ],
[ 'Math_class' => \#Math_class_student_names ],
) {
my ($class_name, $student_names) = #$_;
$register{$class_name} = {
map { $_+1 => $student_names->[$_] }
0..$#$student_names
};
}

How to read dictionary with child

Help me in reading this dictionary: (its an NSDictionary generated from XMLData)
Menus = {
Table1 = {
Phone = {
text = "\n (404) 371-1466";
};
text = "\n ";
};
text = "\n";
};
}
This should get you the phone number, for example:
NSString *phoneNum = [[[[dict objectForKey:#"Menus"] objectForKey:#"Table1"]
objectForKey:#"Phone"] objectForKey:#"text"];
You have one item in the dictionary - "Menus".
That single item is a dictionary, with two items - "Table 1" and "text". "Table 1" is a dictionary, "text" is an NSString.
Inside "Table 1", you have two items - "Phone" and "text". At this point, I think you can see the pattern...