I have a string of the following format (the number of lines in this string may vary):
$content = @"
key1
value1
key2
value2
key3
value3
"@
I want to put this data in a hashtable.
(In my case, the data in the $content
variable is received in the body of the HTTP response from the Invoke-WebRequest cmdlet by Client/Server Protocol in the 'LiveJournal'. But I am interested in the answer to my question for the general case as well.)
I tried to use the cmdlet ConvertFrom-StringData, but it doesn't work for this case:
PS C:\> ConvertFrom-StringData -StringData $content -Delimiter "`n"
ConvertFrom-StringData: Data line 'key1' is not in 'name=value' format.
I wrote the following function:
function toHash($str) {
$arr = $str -split '\r?\n'
$hash = @{}
for ($i = 0; $i -le ($arr.Length - 1); $i = 2) {
$hash[$arr[$i]] = $arr[$i 1]
}
return $hash
}
This function works well:
PS C:\> toHash($content)
Name Value
---- -----
key3 value3
key2 value2
key1 value1
My question is: is it possible to do the same thing, but shorter or more elegant? Preferably in one-liner (see the definition of this term in the book 'PowerShell 101'). Maybe there is a convenient regular expression for this case?
CodePudding user response:
As commented by @Santiago Squarzon;
The code you already have looks elegant to me, shorter
-ne
more elegant
For the "Preferably in one-liner", what exactly is definition of a one line:
- A single line, meaning a text string with no linefeeds?
- Or a single statement meaning a text string with no linefeeds and no semicolons?
Knowing that there are several ways to cheat on this, like assigning a variable in a condition (which is hard to read).
Anyways, a few side notes:
The snippet you show might have a pitfall if you have an odd number of lines and Set-StrictMode -Version Lastest
enabled:
Set-StrictMode -Version Latest
toHash "key1`nvalue1`nkey2"
OperationStopped:
Line |
5 | $hash[$arr[$i]] = $arr[$i 1]
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Index was outside the bounds of the array.
Name Value
---- -----
key1 value1
The variable neme $content
, suggests that you reading the data from a file, possibly with Get-Connent
. If that is indeed the case you might consider to stream the input (which conserves memory):
$Content -split '\r?\n' | # Get-Content .\Data.txt
Foreach-Object -Begin {
$Hash = @{}
$Key = $Null
} -Process {
if (!$Key) {
$Key = $_
}
else {
$Hash[$Key] = $_
$Key = $Null
}
} -End {
$Hash
}
And if you create use an [ordered]
dictionary, you might even put this is a single statement like:
$Content -split '\r?\n' |Foreach-Object { $h = [Ordered]@{} } { if (!$h.count -or $h[-1]) { $h[$_] = $Null } else { $h[$h.Count - 1] = $_ } } { $h }
(Note that -as with the propose in the question- I do not take into account that there might be empty lines in the input data)
See also PowerShell issue: #13817
Enhance hash table syntax