PowerTip: Use PowerShell to Search a Hash Table


Summary: Quickly search a hash table in Windows PowerShell.

Hey, Scripting Guy! Question How can I easily search for information in a hash table full of data in Windows PowerShell?

Hey, Scripting Guy! Answer Plug in the name of a value, for example:

[array]$Hashtable=$NULL
$Hashtable+=@{Purple=54}
$Hashtable+=@{People=37}
$Hashtable+=@{Eater=78}

To find the value called People, add the name to the hash table variable:

$Hashtable.People

37

Comments (18)

  1. @jrv

    Love the feedback!  Just like the Nutrimatic but MUCH more useful 🙂

    "Share and Enjoy, Share and Enjoy…"

    Keep it coming!

    Sean

  2. jrv says:

    @Sean – All

    I tried to point out that it is an aray and not a hashtable.  It behaves like a hashtable doue to some internal NET magic.

    Try this:

    [array]$ht=$null

    $ht.GetType()

    $ht+=@{name='joe'}

    $ht|gm

    $ht.GetType()

    Now ….

    [hashtable]$ht=$null

    $ht.GetType()

    $ht+=@{name='joe'}

    $ht|gm

    $ht.GetType()

    Notice that on both the 'GetType' throws and error after the creation which is why we usually do this:

    [array]$ht=@{}

    $ht.GetType()

    OR

    [hashtable]$ht=@{}

    $ht.GetType()

    Now we get no errors and the type difference holds.

    This is one major difference:

    [array]$ht=@{}

    $ht+=@{Name='joe'}

    $ht+=@{Name='joe'}

    $ht

    To store unique name/value pairs use a raw hashtable.  To store non-unique name/value pairs then use an array of hashtables either [array] or [hashtable[]]

    Now look at how this behaves:

    [hashtable]$ht=@{}

    PS >$ht+=@{Name='joe'}

    PS >$ht+=@{PS C:scripts> [hashtable[]]$ht=@{}

    PS >$ht+=@{Person='joe'}

    PS >$ht+=@{Person='sam'}

    PS >$ht+=@{Animal='cat'}

    PS >$ht.'Person'

    joe

    sam

    Note how convenient that can be,

  3. jrv says:

    @XrstalLens

    It works fine for me as you can see by the examples I posted.  You are missing patches maybe?

  4. @All

    Wow!  What a lot of great chatter on this one!

    What I've done to get us the best answer is consult the Great Council of PowerShell Elders and from what they come back with I will formulate into an independent post (or maybe posts) on "Hey Scripting Guys!"

    Cheers all!

    Sean

  5. jrv says:

    Useful alternate forms:

    $Hashtable=@{

        Purple=54

        People=37

        Eater=78

    }

    [hashtable]$Hashtable=@{}

    $Hashtable.Add('Purple',54)

    $Hashtable.Add('People',37)

    $Hashtable.Add('Eater',78)

    We can also generate tables from a file or a string.

  6. jrv says:

    @XrstalLens

    Actually the code works correctly.  An array loaded with Name/Value pairs is a hashtable.

    See:

    PS C:scripts> [array]$Hashtable=$NULL

    PS C:scripts> $Hashtable+=@{Purple=54}

    PS C:scripts> $Hashtable+=@{People=37}

    PS C:scripts> $Hashtable+=@{Eater=78}

    PS C:scripts> $Hashtable.People

    37

    PS C:scripts>

    But is does not work the same way in PowerShell V2 or earlier or it the net Framework is not patched correctly.

  7. jrv says:

    Try this:

    [array]$Hashtable=$NULL

    $Hashtable+=@{Purple=54}

    $Hashtable+=@{People=37}

    $Hashtable+=@{Eater=78}

    $Hashtable+=@{Purple=54}

    $Hashtable+=@{People=37}

    $Hashtable+=@{Eater=78}

    $Hashtable.People

    Now try this:

    [hashtable]$Hashtable=$NULL

    $Hashtable+=@{Purple=54}

    $Hashtable+=@{People=37}

    $Hashtable+=@{Eater=78}

    $Hashtable+=@{Purple=54}

    $Hashtable+=@{People=37}

    $Hashtable+=@{Eater=78}

    $Hashtable.People

    The difference is what makes the hashtable unique

  8. David Wyatt says:

    "In PowerShell 4.0 (and possibly 3) once the [Array] is populated with values that are apparent to be a hashtable, the destination object is no longer an array (if you run a GET-MEMBER against it you'll see)"

    It's still an array.  If you run "$Hashtable | Get-Member", PowerShell will be sending each element in the array to Get-Member, not the array itself.  You can run "Get-Member -InputObject $Hashtable" , or run this code to see the difference:

    [array]$Hashtable=$NULL

    $Hashtable+=@{Purple=54}

    $Hashtable+=@{People=37}

    $Hashtable+=@{Eater=78}

    # Type of the collection

    $Hashtable.GetType().FullName

    # Type of the members

    $Hashtable | ForEach-Object { $_.GetType().FullName }

    The reason that the script worked as written (but not in PowerShell v2) is because of PowerShell v3's Member Enumeration feature.  It's what allows you to do things like "(Get-Service).Name" instead of "Get-Service | Select-Object -ExpandProperty Name".

  9. jrv says:

    @Complaints –

    Actually the array of has tables let us create a searchable non-unique collection very easily. Of course a list or sorted list might be better but they lack name/value and the dictionaries are also keyed collections.  The best elements to use if we do not care about performance are data tables as the data table has many more features like sorting and filtering.

  10. jrv says:

    @Sean

    What I like about "Tips" is that they become like a little database of ideas.  Each one and its comments add experience/knowledge to the list.

    Keep the ideas coming.

  11. @XrstalLens @jrv and @david

    Good point to note is as was said below.  Also [array] works the CORRECT implementation should be [hashtable] to maintain a consistent approach across all environments.  

    In PowerShell 4.0 (and possibly 3) once the [Array] is populated with values that are apparent to be a hashtable, the destination object is no longer an array (if you run a GET-MEMBER against it you'll see)

    Sean

  12. jrv says:

    @Sean – great!

    Don't forget to factor in Generics too.

  13. jrv says:

    @XrstalLens

    You are missing Net Framework patches.

    We might use an array of hashes because of the behavioral ability of arrays of hashes:

    The following works with an [array] of hashes but cannot work with a [hashtable[]].  Try it and you will see but not until you patch your Framework.

  14. David Wyatt says:

    Even if it works, that syntax puts your performance in the toilet.  The way you should append to a hashtable is either by calling $Hashtable.Add(key, value) , or just doing $Hashtable[key] = value.

    [hashtable]$ht1 = $null

    $ht2 = @{}

    Measure-Command {

       for ($i = 0; $i -lt 10000; $i++)

       {

           $ht1 += @{$i = $i}

       }

    } | Select-Object -ExpandProperty TotalMilliseconds

    Measure-Command {

       for ($i = 0; $i -lt 10000; $i++)

       {

           $ht2[$i] = $i

       }

    } | Select-Object -ExpandProperty TotalMilliseconds

    <#

    Output:

    8347.915

    64.6704

    #>

    More than 100 times slower using the += operator at 10000 elements, and it gets worse from there.

    The same goes for arrays.  If you're using the += operator to add elements to an array, you'll be fine if you're only working with a few elements, but for large datasets, you'll be waiting for quite a while:

    $array1 = @()

    $list = New-Object System.Collections.Generic.List[int]

    Measure-Command {

       for ($i = 0; $i -lt 20000; $i++)

       {

           $array1 += $i

       }

    } | Select-Object -ExpandProperty TotalMilliseconds

    Measure-Command {

       for ($i = 0; $i -lt 20000; $i++)

       {

           $list.Add($i)

       }

       $array2 = $list.ToArray()

    } | Select-Object -ExpandProperty TotalMilliseconds

    <#

    Output:

    14065.7015

    55.3543

    #>

  15. David Wyatt says:

    If you really want a single key to map to multiple values, a hashtable containing arrays would perform better than an array containing a bunch of single-element hashtables (which may as well just be PSCustomObjects; you're not actually using the functionality of a hashtable for anything)

  16. XrstalLens says:

    There is a typo in your example. The first line should be:

    [hashtable]$Hashtable=$NULL

    instead of using [array]. As written, the last line throws an exception:

    "Property 'People' cannot be found on this object. Make sure that it exists."

  17. XrstalLens says:

    @jrv – In the generic sense, yes, an array of name-value pairs is a hashtable. But [array] maps to System.Array while[hashtable] (and the @{} syntax) maps to System.Collections.Hashtable. So the example is creating a System.Array containing System.Collections.Hashtables, not a System.Collections.Hashtable with multiple keys, and System.Array doesn't provide access to the keys of its members.

    I entered your examples exactly as you provided them and still get the exception. I'm running PS 3.0 on Windows 7. Unless PS somehow converts a System.Array of System.Collections.Hashtables into a System.Collections.Hashtable I don't see how this could work!

  18. XrstalLens says:

    Well, I tried it on three different (fully patched) machines with PS 3.0 (Windows 7, Server 2012, and Windows 8). I can't get it to work.

    However, I'll concede that out there is some way to make it work. I can't see how someone would run into this because it doesn't make sense to me why one would use an array of hashtable objects to make a hashtable when you could just use hashtable directly.

Skip to main content