Convert Colon Separated String to a PowerShell Dictionary - powershell

I'm trying to convert Colon Separated String to a PowerShell Dictionary. Following are the strings.
$inputkeyvalues = "Appsetting:true|environment:prod"
I have two key value pairs's in the $inputkeyvalues variable and those are separated by pipe delimiter.
first one is: Appsetting:true
second one is: environment:prod
and I'm trying to convert to PowerShell dictionary. The final output should be something like,
Key Value
----- -----
Appsetting true
environment prod
$Dictionary= New-Object "System.Collections.Generic.Dictionary``2[System.String,System.String]"
Can someone please suggest me possible solution for this. Thanks in advance.

Use a hashtable:
$inputkeyvalues = "Appsetting:true|environment:prod"
# Create hashtable
$Dictionary = #{}
# Split input string into pairs
$inputkeyvalues.Split('|') |ForEach-Object {
# Split each pair into key and value
$key,$value = $_.Split(':')
# Populate $Dictionary
$Dictionary[$key] = $value
}

I stripped the #{ and } characters of the string then created the dictionary key value pairs.
$totalDict=#{}
Foreach ($itemDictString in $tempObj.tag_instances)
{
$Dictionary=#{}
$itemDictString.Replace('#{','').Replace('}','').Split(';') | ForEach-Object{
$key,$value=$_.Split('=').Trim()
$Dictionary[$key]=$value
}
$key="tag_id"
$composite_key="$($tempObj.first_name) $($tempObj.last_name) $($tempObj.id) $($Dictionary[$key])"
Write-Host $composite_key
if($totalDict.ContainsKey($composite_key) -eq $true)
{
$totalDict[$composite_key]=$totalDict[$composite_key]+1
}
else
{
$totalDict.Add($composite_key,1)
}
}

Related

What is '#{}' meaning in PowerShell

I have line of scripts for review here, I noticed variable declaration with a value:
function readConfig {
Param([string]$fileName)
$config = #{}
Get-Content $fileName | Where-Object {
$_ -like '*=*'
} | ForEach-Object {
$key, $value = $_ -split '\s*=\s*', 2
$config[$key] = $value
}
return $config
}
I wonder what #{} means in $config = #{}?
#{} in PowerShell defines a hashtable, a data structure for mapping unique keys to values (in other languages this data structure is called "dictionary" or "associative array").
#{} on its own defines an empty hashtable, that can then be filled with values, e.g. like this:
$h = #{}
$h['a'] = 'foo'
$h['b'] = 'bar'
Hashtables can also be defined with their content already present:
$h = #{
'a' = 'foo'
'b' = 'bar'
}
Note, however, that when you see similar notation in PowerShell output, e.g. like this:
abc: 23
def: #{"a"="foo";"b"="bar"}
that is usually not a hashtable, but the string representation of a custom object.
The meaning of the #{}
can be seen in diffrent ways.
If the #{} is empty, an empty hash table is defined.
But if there is something between the curly brackets it can be used in a contex of an splatting operation.
Hash Table
Splatting
I think there is no need in explaining what an hash table is.
Splatting is a method of passing a collection of parameter values to a command as unit.
$prints = #{
Name = "John Doe"
Age = 18
Haircolor = "Red"
}
Write-Host #prints
Hope it helps! BR
Edit:
Regarding the updated code from the questioner the answer is
It defines an empty hash table.
Be aware that Get-Content has its own parameters!
THE MOST IMPORTANT 1:
[-Raw]

Casting Object to String Array Powershell

I want to create an array of strings instead of a variable object so that I can use the "contains" keyword on each index of the array.
$myArray = Get-ADDomain
The above creates an object, which is not what I want. I also tried
[string[]] $myArray = Get-ADDomain
But after that, $myArray only contains one string and it is the first non-empty property of Get-ADDomain, in my case "ComputersContainer". What should I do to receive an array of strings where each string is a different property, such as
$myArray[0] = "AllowedDNSSuffixes = {}"
PowerShell will always return objects by design of course, and specifying that [string[]], does not really change that.
For what you are trying to use, you have to force the array creation. The below is just one way, but I am sure others will have more elegant ways of doing this as well. Though I am curious why one would want to do this, this way. But, hey, that's just me.
# Create an empty array
$DomainData = #()
# Get all the data points for the utilized cmdlet, split on a common delimiter for the array
[string[]]$DomainData = (Get-ADDomain | Select *) -split ';'
# Display the array count
$DomainData.Count
34
# validate getting a value from the array by using an index number
$Item = $DomainData[17]
NetBIOSName=CONTOSO
[array]::IndexOf($DomainData, $Item)
17
# Use that element number to validate the use of the contains comparison operator
0..($DomainData.Count - 1) | %{ If($DomainData[$_] -contains $item){"Index key is $_ contains a value of $Item"} }
Index key is 17 contains a value of NetBIOSName=CONTOSO
# Use the previous with a partial string for a comparison, -contains cannot be used, like or match has to be used
# From the documentation:
# -Contains
# Description: Containment operator. Tells whether a collection of reference values includes a single test value.
$Item = '*domain*'
0..($DomainData.Count - 1) | %{ If($DomainData[$_] -like $item){"Index key is $_ like a value of $Item"} }
Index key is 1 like a value of *domain*
Index key is 6 like a value of *domain*
Index key is 7 like a value of *domain*
Index key is 8 like a value of *domain*
Index key is 18 like a value of *domain*
Index key is 20 like a value of *domain*
You cannot cast a PSObject directly to a string array like that.
However, this can be accomplished rather easily.
To get an array of string from the object
$myArray = Get-ADDomain
# You can use a standard array #() but these tends to be slower for bigger amount of data
$outArray = New-Object -TypeName System.Collections.Generic.List[String]
#To add just the value
$myArray.psobject.properties | Foreach { $outArray.Add($_.Value) }
# To add Name = {Value} instead
$myArray.psobject.properties | Foreach { $outArray.Add("$($_.Name) = {$($_.Value)}") }
Using an hasthable instead:
$myArray = Get-ADDomain
$hashtable = #{}
$myArray.psobject.properties | Foreach { $hashtable[$_.Name] = $_.Value }
# If you need to do something with the key
Foreach ($key in $hashtable.Keys) {
$Value = $hashtable[$key]
if ($value -like '*prod*') {
Write-Host $key
}
}

How to allow unspecified named parameters? [duplicate]

Using [parameter(ValueFromRemainingArguments=$true)] one can get all the remaining arguments passed to the function into a variable as a list.
How can I get the remaining arguments as a hashtable type, for example for inputs like Function -var1 value1 -var2 value2?
There are multiple ways to achieve this. The following solution supports parameters with:
Simple value (single item)
Array value
Null value (switch)
Script:
function testf {
param(
$name = "Frode",
[parameter(ValueFromRemainingArguments=$true)]
$vars
)
"Name: $name"
"Vars count: $($vars.count)"
"Vars:"
#Convert vars to hashtable
$htvars = #{}
$vars | ForEach-Object {
if($_ -match '^-') {
#New parameter
$lastvar = $_ -replace '^-'
$htvars[$lastvar] = $true
} else {
#Value
$htvars[$lastvar] = $_
}
}
#Return hashtable
$htvars
}
testf -simplepar value1 -arraypar value2,value3 -switchpar
Output:
Name: Frode
Vars count: 5
Vars:
Name Value
---- -----
arraypar {value2, value3}
switchpar
simplepar value1
Edit: Modified default value assigned to Hashtable keys: $htvars[$lastvar] = $true. Using $true as the default accounts for switch parameters and can make the resulting Hastable more "splattable".
Updated per Ansgars comment.
One possibility is to build the hash-table within the function. Here is an example:
function MyFunction
{
[CmdletBinding()]
param([parameter(ValueFromRemainingArguments=$true)] $allparams)
process
{
$myhash = #{}
for ($i = 0; $i -lt $allparams.count; $i+=2)
{
$myhash[($allparams[$i]-replace '^-+')] = $allparams[$i+1]
}
}
end
{
$myhash
}
}
We parse through the params in the parameters $allparams using a for loop, and retrieve the key/value pairs to form the hash table, then in the end block we display it.
MyFunction -var1 10 -var2 30 -var3 hello
Name Value
---- -----
var1 10
var3 hello
var2 30
This is tricky because using [Parameter(ValueFromRemainingArguments=$true)] means this is an advanced function, and $args cannot be used in advanced functions.
But if you want a hashtable of all the specified parameters and their values, you could simply use $PSBoundParameters, like so :
function foo {
[cmdletbinding()]
param(
[Parameter(Position=0)]
$Name,
[Parameter(Position=1,ValueFromRemainingArguments=$true)]
$LastName
)
"PSBoundParameters : "
$PSBoundParameters
}
foo -Name Mike Jordan Jones
This results in the following :
PSBoundParameters :
Key Value
--- -----
Name Mike
LastName {Jordan, Jones}
Is this what you are trying to achieve ?
There are a number of caveats with this method but I just wanted to show that ConvertFrom-StringData could also be considered here.
function Convert-StringToHashTable {
param(
[parameter(ValueFromRemainingArguments=$true)]
[string]$string
)
$string -replace "(^| )-","`r`n" | ForEach-Object{$_ -replace "(\w+) (.*)",'$1=$2'} | ConvertFrom-StringData
}
Convert-StringToHashTable -var1-5 value1 -var2 "value2 or 3"
Output from above would be
Name Value
---- -----
var2 value2 or 3
var1-5 value1
We take the all the remaining arguments as a single string. Then we split the string on - that occur at the beginning of the line or after a space. ( This accounts for dashes in the middle of works like Ansgar mentioned in another answer). Then we convert the first space after the first word into an equal sign. That will make a string of key value pairs that ConvertFrom-StringData expects.
Known Caveats
This will not work if you try to send arrays like in Frode's answer. He can handle those.

How do I retrieve a HashTable key from a value?

I have a PowerShell HashTable that contains a set of key-value pairs (naturally). All of the HashTable values are unique.
I would like to retrieve a HashTable key, based on a value that I specify, using PowerShell.
Another options:
to iterate over the HashTable keys and find a key that contains the
value:
$HashTable.Keys |? { $HashTable[$_] -eq $Val }
to iterate using GetEnumerator() function:
$HashTable.GetEnumerator() | ?{ $_.Value -eq $Val } | %{ $_.Key }
You can use PowerShell 4.0's Where method syntax to achieve this. The Where method accepts a PowerShell ScriptBlock to find objects matching the specified criteria. We can iterate over the HashTable keys and find a key that contains the desired value.
In case you do have a scenario where you have duplicate HashTable values, you can optionally specify a second parameter, of type WhereOperatorSelectionMode, that specifies which objects should be returned by the call to the Where method. By specifying First for the second method parameter, we can ensure that only a single HashTable key is ever returned.
All of the supported values for the second parameter are as follows:
Default
First
Last
SkipUntil
Until
Split
$HashTable = #{
1 = 10;
2 = 20;
3 = 30;
}
$Val = 30;
$HashTable.Keys.Where({ $HashTable[$PSItem] -eq $Val; }, [System.Management.Automation.WhereOperatorSelectionMode]::First);

Get ValueFromRemainingArguments as an hashtable

Using [parameter(ValueFromRemainingArguments=$true)] one can get all the remaining arguments passed to the function into a variable as a list.
How can I get the remaining arguments as a hashtable type, for example for inputs like Function -var1 value1 -var2 value2?
There are multiple ways to achieve this. The following solution supports parameters with:
Simple value (single item)
Array value
Null value (switch)
Script:
function testf {
param(
$name = "Frode",
[parameter(ValueFromRemainingArguments=$true)]
$vars
)
"Name: $name"
"Vars count: $($vars.count)"
"Vars:"
#Convert vars to hashtable
$htvars = #{}
$vars | ForEach-Object {
if($_ -match '^-') {
#New parameter
$lastvar = $_ -replace '^-'
$htvars[$lastvar] = $true
} else {
#Value
$htvars[$lastvar] = $_
}
}
#Return hashtable
$htvars
}
testf -simplepar value1 -arraypar value2,value3 -switchpar
Output:
Name: Frode
Vars count: 5
Vars:
Name Value
---- -----
arraypar {value2, value3}
switchpar
simplepar value1
Edit: Modified default value assigned to Hashtable keys: $htvars[$lastvar] = $true. Using $true as the default accounts for switch parameters and can make the resulting Hastable more "splattable".
Updated per Ansgars comment.
One possibility is to build the hash-table within the function. Here is an example:
function MyFunction
{
[CmdletBinding()]
param([parameter(ValueFromRemainingArguments=$true)] $allparams)
process
{
$myhash = #{}
for ($i = 0; $i -lt $allparams.count; $i+=2)
{
$myhash[($allparams[$i]-replace '^-+')] = $allparams[$i+1]
}
}
end
{
$myhash
}
}
We parse through the params in the parameters $allparams using a for loop, and retrieve the key/value pairs to form the hash table, then in the end block we display it.
MyFunction -var1 10 -var2 30 -var3 hello
Name Value
---- -----
var1 10
var3 hello
var2 30
This is tricky because using [Parameter(ValueFromRemainingArguments=$true)] means this is an advanced function, and $args cannot be used in advanced functions.
But if you want a hashtable of all the specified parameters and their values, you could simply use $PSBoundParameters, like so :
function foo {
[cmdletbinding()]
param(
[Parameter(Position=0)]
$Name,
[Parameter(Position=1,ValueFromRemainingArguments=$true)]
$LastName
)
"PSBoundParameters : "
$PSBoundParameters
}
foo -Name Mike Jordan Jones
This results in the following :
PSBoundParameters :
Key Value
--- -----
Name Mike
LastName {Jordan, Jones}
Is this what you are trying to achieve ?
There are a number of caveats with this method but I just wanted to show that ConvertFrom-StringData could also be considered here.
function Convert-StringToHashTable {
param(
[parameter(ValueFromRemainingArguments=$true)]
[string]$string
)
$string -replace "(^| )-","`r`n" | ForEach-Object{$_ -replace "(\w+) (.*)",'$1=$2'} | ConvertFrom-StringData
}
Convert-StringToHashTable -var1-5 value1 -var2 "value2 or 3"
Output from above would be
Name Value
---- -----
var2 value2 or 3
var1-5 value1
We take the all the remaining arguments as a single string. Then we split the string on - that occur at the beginning of the line or after a space. ( This accounts for dashes in the middle of works like Ansgar mentioned in another answer). Then we convert the first space after the first word into an equal sign. That will make a string of key value pairs that ConvertFrom-StringData expects.
Known Caveats
This will not work if you try to send arrays like in Frode's answer. He can handle those.