Home > database >  How to get a hashtable in PowerShell from a multiline string in which keys and values are on differe
How to get a hashtable in PowerShell from a multiline string in which keys and values are on differe

Time:01-28

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

  • Related