Easily Create a PowerShell Hash Table

 Summary: Learn how to automatically populate a hash table in a Windows PowerShell script.


Microsoft Scripting Guy Ed Wilson here. A hash table is an important data structure in Windows PowerShell. Many of the cmdlets use hash tables to format their input. For example, if I want to create a custom column header in a table, I have to use a hash table. A hash table consists of one or more key value pairs (of course, it is possible to create an empty hash table that contains no key value pairs, but let’s go with the easy description first).

The at sign and a pair of braces (curly brackets) identify a hash table. Normally a variable stores the hash table, but it is possible to create a hash table and not store it in a variable. An example of this is shown here:


“key1” = “value1”

“key2” = “value2”


In the following figure, I first run the code and display the contents of the hash table. Next, I pipe the results to the Get-Member cmdlet.

Image of contents of hash table

Most of the time, a hash table is stored in a variable for use in other places. It is possible to create a hash table on a single line, but it is difficult to read, and if a problem occurs, it is hard to troubleshoot. The semicolon separates key value pairs and indicates a new line. In the code seen here, I create a hash table on a single line.

$hash = @{“key1” = “value1″;”key2” = “value2”}

The same hash table is easier to read when spread out on multiple lines. This technique is shown here:

$hash1 = @{

  “key1” = “value1”

  “key2” = “value2”


Whether the closing brace appears on its own line or after the final key value pair is a matter of stylistic taste. I generally prefer to place it on line because it is easier to spot when troubleshooting. When working with a script editor that automatically matches brace pairs, this advantage disappears, and I then prefer to close up the code.

The real power of hash tables comes by adding key value pairs automatically from within the script. When used in this way, hash tables are essentially temporary data storage. The advantage a hash table has over an array is the key value pairs. The keys provide a way to retrieve the associated value by name; with an array, the value is accessible via the element number. A disadvantage over an array is that with a hash table, the key must be unique; an array permits multiple elements to be the same.

To create a hash table dynamically, follow these steps:

1.       Create an empty hash table.

2.       Store the empty hash table in a variable.

3.       Collect the data.

4.       Store the collected data in a variable.

5.       Use the foreach statement to walk through the collected data.

6.       Inside the loop call the add method to add the key value pairs to the hash table.

An example of this procedure is shown here:

$hash = $null

$hash = @{}

$proc = get-process | Sort-Object -Property name -Unique


foreach ($p in $proc)




The first thing I do is assign the value $null to the $hash variable. I do this because when running code multiple times in the Windows PowerShell ISE, the values of global variables continue to be present. If I am not paying attention, the value stored in variables can change with each run of the script. After I have initialized the $hash variable with $null, I create an empty hash table and store it in the $hash variable. These two lines of code are shown here:

$hash = $null

$hash = @{}

Next I use the Get-Process cmdlet to collect information about each process that is running on the computer. I sort these process objects based upon the name property, and I use the unique switched parameter to return only unique instances of the process objects. The reason for doing this is I want to use the name property as the value for the keys in my hash table. The key of a hash table must be unique, and in most cases, there are several duplicate instances of processes running on a computer. For example, the following code reveals there are several processes named svchost.


C:\Users\edwils> gps | ? {$_.name -eq ‘svchost’}



Handles             NPM(K)             PM(K)               WS(K)               VM(M)              CPU(s)               Id ProcessName


    702               20                     10324               13464               56                     612                   svchost

    443               15                     6328                 11808               53                     936                   svchost

    165               12                     5512                 10580               48                     1036                 svchost

    653               28                     28756               28172               93                     1120                 svchost

    789               31                     16132               26284               119                   1152                 svchost

   2625              127                   97160               88144               438                   1184                 svchost

    798               37                     19064               22760               154                   1292                 svchost

    339               34                     16332               19100               92                     1312                 svchost

    599               29                     9136                 16152               68                     1348                 svchost

    615               28                     11352               15524               59                     1388                 svchost

    105               9                      2900                 6644                 35                     2052                 svchost

     86                8                      2596                 5660                 47                     2332                 svchost

    350               15                     7444                 9340                 47                     4256                 svchost

    524               22                     8832                 11304               56                     4812                 svchost

     50                4                      1520                 3336                 13                     5560                 svchost


When I use the unique switched parameter, I retrieve only one instance of each process with the same name. I do not know which instance I obtain, but for this application, it does not matter. This line of code is shown here:

$proc = get-process | Sort-Object -Property name -Unique

Now it is time to walk through the collection of process objects and add the name and path to the hash table. I use the name of the process for the key and the process ID as the value. The foreach statement is thebest command to use to walk through the collection of process objects. I use the add method from the hashtable object that is stored in the $hash variable. The add method requires both the key and the value. This portion of the code is shown here:

foreach ($p in $proc)




When I display the contents of the $hash variable, I am presented with the following output.

Image of contents of $hash variable


That is all there is to dynamically creating a hash table.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.


Ed Wilson, Microsoft Scripting Guy



Comments (6)

  1. great blog, congratulations

  2. Ed WIlson says:

    @Leonardo thank you. I am glad you like it.

  3. chaitanya says:


    When a script that takes Hashtable type parameter as input, how do I pass input values to this type? I tried to execute such a script from ISE and when the input value is specified as @{"option1"="value1"; "option2"="value2"} then I get the error as below

    Cannot process argument transformation on parameter 'hashtableParam'. Cannot convert the "@{"option1"="value1";"option2"="value2"}" value of type "System.String" to type "System.Collections.Hashtable".

  4. Wesley says:

    an ampersand is not the @ symbol.

    & is an ampersand

    Other than that, great article

  5. mredwilson says:

    @Wesley, thanks for the catch, I just corrected the post.

  6. Rob Ingenthron says:

    Hash tables (aka "Dictionaries" in VBscript) are really great for optimizing searches through a large set of key-value pairs rather than doing a loop through a large array over and over and over…

    Every article regarding the use of hash tables refer to really small scale usage, and usually pre-created key-value pairs. When you don’t know what the keys will be, and the data set is really large (e.g. 15,000 user objects from AD), there is never any discussion
    of the performance or memory usage.

    After setting a variable with Get-ADUser to get all AD user objects (15,000+ user objects), doing a simple loop that simply adds each user object (sAMAccountName – department) to the hash table, it takes SOOOOOOO long to iterate through all user objects. It’s
    magnitudes slower in performances as compared to VBscript dictionaries. It’s crazy that it takes over 40 minutes to iterate that simple loop, when, in VBscript, it would take about 5 minutes, if that.

    There’s no doubt that PowerShell is great for slapping together commandlines or small scripts rather than coding something in VBscript, but for extensive scripting, especially where large hash tables (or multi-dimensional hash tables) are involved, PowerShell
    is a poor choice, and the more I use it, the less it looks like a good scripting tool for anything other than quickie scripts and simple processes, especially given it’s many "quirks" and need to know .NET to get more efficiency.

    With that said, Internet searches have proved futile, so I’d love to see any comments (or new blog posts) regarding increasing performance of hash tables or some other method that can be leveraged within PowerShell to get the same VBscript-like Dictionary functionality.
    Is there a way to quickly populate a hash table from the Get-ADUsers cmdlet??


    — Rob —

Skip to main content