Im looking for a data structure/cmdlet that will allow me to add multiple values to a single key in Powershell.
My data would ideally look like this:
KEY-------------------------- VALUES
HOSTNAME1-------------DATABASE1,DATABASE2,DATABASE3
HOSTNAME2-------------DATABASE1,DATABASE2
etc...
I thought a hashtable would do the trick, but I'm unable to do the following:
$servObjects = #{}
$servObjects.Add("server1", #())
$servObjects.get_item("server1") += "database1"
This yields an empty array when I try:
$servObjects.get_item("server1")
I have also tried to do the following, hoping that powershell would understand what I want:
$servObjects2 = #{}
$servObjects2.add($servername, $databasename)
This will unfortunately yield a duplicate key exception
Thanks for any and all input
You basically want a hash table with values that are arrays. You don't have to use $hashtable.get_item or .add
$myHashTable = #{} # creates hash table
$myHashTable.Entry1 = #() #adds an array
$myHashTable.Entry1 += "element1"
$myHashTable.Entry1 += "element2"
This results in the following output:
$myHashTable
Name Value
---- -----
Entry1 {element1, element2}
$myHashTable.Entry1
element1
element2
If you have your data in an array you can group the array and convert to a hash table:
$ary = #()
$ary = $ary + [PSCustomObject]#{RowNumber = 1; EmployeeId = 1; Value = 1 }
$ary = $ary + [PSCustomObject]#{RowNumber = 2; EmployeeId = 1; Value = 2 }
$ary = $ary + [PSCustomObject]#{RowNumber = 3; EmployeeId = 2; Value = 3 }
$ary = $ary + [PSCustomObject]#{RowNumber = 4; EmployeeId = 2; Value = 4 }
$ary = $ary + [PSCustomObject]#{RowNumber = 5; EmployeeId = 3; Value = 5 }
$ht = $ary | Group-Object -Property EmployeeId -AsHashTable
$ht is then:
Name Value
---- -----
3 {#{RowNumber=5; EmployeeId=3; Value=5}}
2 {#{RowNumber=3; EmployeeId=2; Value=3}, #{RowNumber=4; EmployeeId=2; Value=4}}
1 {#{RowNumber=1; EmployeeId=1; Value=1}, #{RowNumber=2; EmployeeId=1; Value=2}}
In your original example, instead of writing
$servObjects.get_item("server1") += "database1"
you had written
$servObjects.server1 += "database1"
it would have worked.
I'm very new to PowerShell, but I prefer to use
$servObjects.Add("key",#())
over
$servObjects.key = #())
because the .Add will throw a duplicate exception if the key is already present in the hashtable, whereas the assignment will replace an existing entry with a new one. For my purposes, I have found that the implicit replacement is (more often than not) an error, either in my logic, or an anomaly in the input data that needs to be handled.
If you know the value at creation time, it would be clearer this way :
[hashtable]$hash = #{
HOSTNAME1 = #(DATABASE1, DATABASE2, DATABASE3);
HOSTNAME2 = #(DATABASE1, DATABASE2);
}
Which will get you the following :
Name Value
---- -----
HOSTNAME2 {DATABASE1, DATABASE2}
HOSTNAME1 {DATABASE1, DATABASE2, DATABASE3}
Related
I'm able to add fields in the footer of a word document, like this :
$footer = $Section.Footers.Item(1)
$footer.Range.Text = "";
$Footer.Range.ParagraphFormat.Alignment=2
$Range = $Footer.Range
$null = $ExistingDoc.Fields.Add($footer.range, 33) # Page
$Range.SetRange($Range.end + 1,$Range.end + 1)
$null = $Range.InsertBefore(' of ')
$Range.SetRange($Range.end + 1,$Range.end + 1)
$null = $ExistingDoc.Fields.Add($range, 26) # Total Page
$Range.SetRange($Range.star,$Range.start)
$null = $Range.InsertBefore('Page ')
$Footer.Range.ParagraphFormat.Alignment=2
But I'm not able to add a table and put fields in it.
I tried many syntaxes with no luck like :
$table2 = $ExistingDoc.Tables.Add($footer.range,2,3)
$range2 = $table2.cell(1,1).select
$null = $ExistingDoc.Fields.Add($Footer.range.Tables(2), 33)
or
$table2 = $ExistingDoc.Tables.Add($footer.range,2,3)
$null = $ExistingDoc.Fields.Add($table2.cell(1,1), 33)
I did it with a macro and find the following code :
Selection.Fields.Add Range:=Selection.Range, Type:=wdFieldEmpty, Text:= _
"PAGE ", PreserveFormatting:=True
But no luck to convert it to Powershell, any help is welcome.
I have a hash array as such:
$weeklyStats[$RecipientName][$weekNr][$total]
Which is created in a loop as such:
$weeklyStats = #{}
$weekNr = get-date -UFormat %V
ForEach ($RecipientName in $MailTraffic.keys)
{
$weeklyStats[$RecipientName] = #{}
$weeklyStats[$RecipientName][$weekNr] = #{}
$weeklyStats[$RecipientName][$weekNr]['Total'] = 0
$weeklyStats[$RecipientName][$weekNr]['Sent'] = 0
$weeklyStats[$RecipientName][$weekNr]['Received'] = 0
foreach($item in $MailTraffic[$RecipientName].keys)
{
weeklyStats[$RecipientName][$weekNr]['Total'] =+ 1
if $MailTraffic[$RecipientName]['transaction'] == "Sent"
{
$weeklyStats[$RecipientName][$weekNr]['Sent'] =+ 1
}
else
{
$weeklyStats[$RecipientName][$weekNr]['Received'] =+ 1
}
}
}
I don't know how to 'dump' a variable in Powershell but here is the contents in json:
{
"mike": {
"11": {
"Total": 411,
"Sent": 21,
"Received":390,
}
},
"peter": {
"11": {
"Total": 751,
"Sent": 51,
"Received":700,
}
},
"frank": {
"11": {
"Total": 620,
"Sent": 20,
"Received":600,
}
},
}
I want to print out the keys and values in descending order of the $total.
I can only find examples how to do it if the hash table is only one level deep.
The intended output would be:
Name Total Received Sent
----- ----- ----- -----
peter 751 700 51
frank 620 600 20
mike 411 390 21
Sort by referencing the Keys property of the inner hashtable, then assign to a new [ordered] dictionary:
$sorted = [ordered]#{}
$stats.GetEnumerator() |Sort-Object {
# Sort by first key from each inner hashtable
$_.Value.Keys |Select -First 1
} -Descending |ForEach-Object {
# re-assign to our ordered dictionary
$sorted[$_.Key] = $_.Value
}
$sorted now contains your new sorted dictionary
Most PowerShell cmdlets are intended to handle (stream!) [PSObject] type (which includes a [PScustomerObject] type) lists for input and output.
(To understand the difference see e.g. Difference between PSObject, Hashtable, and PSCustomObject).
Nested hash tables are difficult to maintain and handle in PowerShell (see also: Powershell Multidimensional Arrays) because PowerShell is optimized for streaming which is rather difficult with cascaded objects, therefore I recommend you convert you nested hashtable in a (rather flat) [PScustomerObject] list, something like:
$PSStats =
ForEach ($name in $Stats.Keys) {
ForEach ($weekNr in $_.Keys) {
ForEach ($total in $_.Keys) {
[pscustomobject]#{name = $name; weekNr = $weekNr; total = $total}
}
}
}
Once you have converted it into PSCustomObject list, you can easily sort it and display the results:
$PSStats | Sort-Object Total
I would just create a custom object from your hash tables and then sort on the Title property:
# Creation of $mailtraffic
$mailtraffic = #{'Mike' = #{'Total' = 411; 'Sent' = 21; 'Received' = 390};'Peter' = #{'Total' = 751; 'Sent' = 51; 'Received' = 700};'Frank' = #{'Total' = 620; 'Sent' = 20; 'Received' = 600}}
# Sorting Code
$mailtraffic.GetEnumerator() |
Select #{n='Name';e={$_.Key}},#{n='Total';e={$_.Value.Total}},#{n='Received';e={$_.Value.Received}},#{n='Sent';e={$_.Value.Sent}} |
Sort Total -Descending
I have two hash tables and I need to compare them. Let me explain my problem :
[hashtable]$User = #{
"Jack" = "AdminLA, AdminUSA";
"John" = "AdminAustralia";
"Sarah" = "AdminIceland";
"Arnold" = "AdminUSA";
"Maurice" = "AdminAustralia, AdminCanada";
}
[hashtable]$Profil = #{
"AdminLA" = "P1";
"AdminIceland" = "P2";
"AdminUSA" = "P3";
"AdminCanada" = "P4";
"AdminAustralia" = "P5" ;
"AdminCroatia" = "P6";
}
I want to have this kind of result :
Key Value
--- -----
Jack P1, P3
John P5
Sarah P2
Arnold P3
Maurice P5, P4
Actually, I have only one value (I haven't succeeded to have multiple values. For example Jack must have P1 and P3 and I have only P1).
How can I fix it?
I have already tried:
$User.GetEnumerator() | select Key, #{n='Value'; e={$Profil[$_.Value]}}
and
$User.GetEnumerator() | %{[PSCustomObject]#{aKey=$_.Key;bValue=$Profil[$_.Value]}}
Any idea?
Use this expression
$User.GetEnumerator() | Select-Object Key, #{name='Value'; expression={($_.Value -split ", " | Foreach-Object {$Profil[$_]}) -join ", "}}
This basically creates an array of input values, get the values from $Profil for each element and then creates a string from these values.
I have 5 hash tables:
$Monday = #{RUG = "";NRH1 = "";NRH2 = "";ELM = "";BAGVAGT = ""}
$Tuesday = #{RUG = "";NRH1 = "";NRH2 = "";ELM = "";BAGVAGT = ""}
$Wednesday = #{NRH1 = "";NRH2 = "";ELM = "";BAGVAGT = ""}
$Thursday = #{NRH1 = "";NRH2 = "";ELM = "";BAGVAGT = ""}
$Friday = #{NRH1 = "";NRH2 = "";ELM = ""}
That get filled with data. And I can get data out from these either one at at time with $Monday.RUG or all with $Monday | out-string. No problem there.
I'm going to combine those in another hash table 100 times with different data.
So it will be like this:
$Week = #{
1 = #{mo=$Monday;tu=$Tuesday;we=$Wednesday;th=$Thursday;fr=$Friday;val=$value}
2 = #{mo=$Monday;tu=$Tuesday;we=$Wednesday;th=$Thursday;fr=$Friday;val=$value}
}
And so on, until I have 100 different weeks with different values (value will be a calculated number)
But the question is. How do I access the items in the hashtables inside the $week hashtable?
Is there a direct way like $week.1.mo ? or do you need to use a loop?
You can access it using:
$Week[1].mo
I have a theoretical problem - how to reference a hash table during its initialization, for example, to compute a member based other already stated members.
Remove-Variable myHashTable -ErrorAction Ignore
$myHashTable =
#{
One = 1
Two= 2
Three = ??? # following expressions do not work
# $This.One + $This.Two or
# $_.One + $_.Two
# $myHashTable.One + $myHashTable.Two
# ????
}
$myHashTable.Three -eq 3 # make this $true
Any ideas how to do it? Is it actually possible?
Edit:
This was my solution:
$myHashTable =
#{
One = 1
Two= 2
}
$myHashTable.Three = $myHashTable.One + $myHashTable.Two
This won't be possible using the object initializer syntax I'm afraid. While it is possible to use variables, you'll have to compute the values before creating the object.
I cannot recommend this, but you can iterate the initializer twice or more:
(0..1) | %{
$a = #{
One = 1
Two = $a.One + 1
}
}
(0..2) | %{
$b = #{
One = 1
Two = $b.One + 1
Three = $b.Two + 1
}
}
Make sure all calculations are idempotent, i.e. do not depend on a number of iterations.
You can also recur to this...
sometimes when the hashtable is very long
and can be defined only in 2 or three recurrences...
works fine:
$AAA = #{
DAT = "C:\MyFolderOfDats"
EXE = "C:\MyFolderOfExes"
}
$AAA += #{
Data = $AAA.DAT + "\#Links"
Scripts = $AAA.EXE + "\#Scripts"
ScriptsX = $AAA.EXE + "\#ScriptsX"
}
Note in the second part we are just adding ( += ) more items to the first part... but now... we can refer the items in first part
of the hashtable