Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting hashtable to array of strings

Tags:

How can I convert a hashtable to an array of strings? Suppose $l_table is a hashtable. If I try

$l_array = $l_table | format-table

then $l_array is an array, but an array of "FormatEntryData" objects. If I do

[string[]]$l_array = $l_table | format-table

then $l_array is an array of strings, but the strings are all "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData". If I try

$l_array = $l_table | out-string

then $l_array is a single string. I've tried lots of other things, but nothing works, short of manually looping through, which I really don't want to do.

like image 530
David I. McIntosh Avatar asked Jan 28 '14 18:01

David I. McIntosh


1 Answers

David I. McIntosh's own answer works well, but it should be noted that the elements of the resulting array correspond to all lines of the default output, which includes:

  • the empty leading and trailing lines
  • the two header lines (the line with the column names and the separator line)

Out-String simply sends what you'd normally see in the console (terminal) to a string, by default as a single string, and with -Stream as an array of strings.

Here's a variation of David's command that removes both the header and the empty lines:

[string[]] $l_array = ($l_table | Out-String -Stream) -ne '' | select -Skip 2

The rest of this answer shows how to control the specifics of the string representation obtained; it uses PS v3+ syntax and built-in alias % for ForEach-Object for brevity.

Note: For clarity, the sample input hashtable is called $ht (not $l_table) in the following examples; the PSv4+ .ForEach() variants perform better.

  • Get all keys as a string array:

      $ht.Keys | % ToString # or (PSv4+): $ht.Keys.ForEach('ToString')
    
  • Get all values as a string array:

      $ht.Values | % ToString # or (PSv4+): $ht.Values.ForEach('ToString')
    
  • Get a custom representation of key-value pairs, in format <key>=<value>; note that .GetEnumerator() is needed to send the key-value pairs individually through the pipeline; by default, PowerShell passes a hashtable as a whole:

      $ht.GetEnumerator() | % { "$($_.Name)=$($_.Value)" }
    
      # or (PSv4+):
      $ht.GetEnumerator().ForEach({ "$($_.Name)=$($_.Value)" })
    

Note that while .ToString(), which is also applied implicitly during string interpolation (inside "...", an expandable string), works well with primitive .NET types (as well as additional numeric types such as [decimal] and [bigint]); generally, types will just print their full type name, unless their .ToString() method is explicitly overridden to return a more meaningful custom representation (which is what the primitive types do and which is the case with only some of the non-primitive types returned by PowerShell cmdlets).
Also note that using an array(-like data structure) inside an expandable string expands to the (stringified) elements concatenation of its elements with the value of the $OFS preference variable, which defaults to a space char. (e.g., $a='one', 'two'; "$a" expands to 'one two') - see this answer for more information on expandable strings (string interpolation) in PowerShell.

A simple example of choosing a property of a value to represent it in the string:

# Sample hashtable containing a value of a non-built-in type,
# [System.Diagnostics.Process]    
$ht = @{ one = 1; two = Get-Process -ID $PID }

# Use the `.Path` property to represent the value.
$ht.GetEnumerator()  | % { "$($_.Name)=$($_.Value.Path)" }
like image 184
mklement0 Avatar answered Oct 11 '22 10:10

mklement0