PowerTip: Use PowerShell to Search a Hash Table

PowerTip: Use PowerShell to Search a Hash Table

  • Comments 19
  • Likes

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

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • 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.

  • @jrv

    Love the feedback!  Just like the Nutrimatic but MUCH more useful :)

    "Share and Enjoy, Share and Enjoy..."

    Keep it coming!

    Sean

  • @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.

  • 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."

  • @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.

  • 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

  • @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!

  • @XrstalLens

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

  • 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

    #>

  • @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

  • "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".

  • @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,

  • 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.

  • @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.

  • 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)