Hashes and properties

Home  >>  PowerShell  >>  Hashes and properties

Hashes and properties

On January 15, 2007, Posted by , In PowerShell,Quiz, With 1 Comment

I really like the way that PowerShell handily formats pipeline objects when displaying them in the console, eg get-childitem in a file system directory returns a collection of file objects and the output is like the old DOS dir command.

Additionally, I really like the way that hashes behave like objects with named properties. For example:

$a=@{"name"="bob";"age"=42}
$a.name # outputs "bob"
$a.age # outputs 42

Unfortunately, if you have a list/array of hashes with the same keys, you cannot benefit from this built-in formatting. The output comes out in two columns, keyed Name and Value.

I want a way to have the properties of the hash be the keys and the values be the values associated with those keys.

Consider the array of hashes created by this code:

$rnd = new-object Random
$list = @()
1..5 | % {
  $o = @{}
  "keyone","keytwo" | % {
    $o["$_"] = "x$($rnd.next(100))"
  }
  $list += $o
}

This creates an array of five hashes, each with two keys (keyone and keytwo) with random values.

If we type $list in the console, we get something like this:

Name                           Value
----                           -----
keyone                         x11
keytwo                         x33
keyone                         x47
keytwo                         x47
keyone                         x72
keytwo                         x57
keyone                         x85
keytwo                         x9
keyone                         x43
keytwo                         x35

This is not what I want. I want the columns to be keyone and keytwo with the values below.

One way around this is to do something like this (all on one line):

$list | select-object @{Name="keyone";Expression={$_.keyone}},@{Name="keytwo";Expression={$_.keytwo}}

Behold! we get what I want:

keyone                                                      keytwo
------                                                      ------
x72                                                         x71
x18                                                         x6
x59                                                         x60
x37                                                         x39
x63                                                         x10

Unfortunately, this requires me to know which columns I want in advance, and with more than two columns, it gets rather verbose.

A solution, that works well for me, is to write a function that replaces hashes in the pipe with a custom PowerShell object with added properties… the cmdlet add-member comes the rescue:

function convertto-hashobject {
  process {
    if ($_ -is [Collections.IDictionary]) {
      $hash = $_
      $out = new-object object
      foreach ($k in $hash.Keys) {
        $out = add-member -i $out -type "noteproperty" -name "$k" -force -passthru $hash[$k]
      }
      $out
    }
    else {
      $_
    }
  }
}

Now, regardless of the hash contents, we can do this:

$list | convertto-hashobject 

to get this:

keyone                                                      keytwo
------                                                      ------
x72                                                         x71
x18                                                         x6
x59                                                         x60
x37                                                         x39
x63                                                         x10

Now, this post is tagged as a Quiz, because I’m sure there is a better way of doing this. Perhaps something blindingly obvious that I missed, or something really cunning.

Let me know!

One Comment so far:

  1. Andrew Savinykh says:

    Well, I kind of asked the same question back in august on the microsoft powershell newsgroup. Here is a cached copy, since the original post seemed to be expired: http://www.vista64.net/forums/showthread.php?t=12840

    I got no reply, but later in another thread I got this:
    http://www.vista64.net/forums/showthread.php?t=13083

    So basically, there is no better way of doing this as of august last year.