In your example, you've depicted multiple custom object/class instances, not a hash table. Since you've mentioned the desire to index by food name, I suggest a hash table with food name keys and custom object/class instance values representing the various food properties. For example: class FoodProperties { [int] $Calories [int] $Fat [decimal] $Price } $food = @{ Ham = [FoodProperties] @{ Calories = 100; Fat = 10; Price = 1.00 } Eggs = [FoodProperties] @{ Calories = 200; Fat = 20; Price = 2.00 } } # Retrieve Ham. $food['Ham'] # Change Ham's price. $food['Ham'].Price = 5.00 # Add a new food. $food['Cheese'] = [FoodProperties] @{ Calories = 300; Fat = 30; Price = 3.00 } Answer from surfingoldelephant on reddit.com
🌐
Microsoft Learn
learn.microsoft.com › en-us › powershell › module › microsoft.powershell.core › about › about_hash_tables
about_Hash_Tables - PowerShell | Microsoft Learn
January 19, 2025 - A hashtable, also known as a dictionary or associative array, is a compact data structure that stores one or more key-value pairs. For example, a hashtable might contain a series of IP addresses and computer names, where the IP addresses are ...
🌐
Microsoft Learn
learn.microsoft.com › en-us › powershell › scripting › learn › deep-dives › everything-about-hashtable
Everything you wanted to know about hashtables - PowerShell | Microsoft Learn
March 9, 2026 - I used ages in all those examples and the key was the person's name. This is a great way to look at it when your collection of objects each have a name. Another common way to use hashtables in PowerShell is to hold a collection of properties where the key is the name of the property.
🌐
tommymaynard.com
tommymaynard.com › home › hash table with embedded hash table and array
Hash Table with Embedded Hash Table and Array - tommymaynard.com
January 6, 2023 - It’s still an array or a list, but now, each item has an association. Each item in the array contains a key and a value. Take a look, keeping in mind that PowerShell will label the keys with “Name” (while the value will be labeled with “Value”). $Hashtable = @{'mid1' = 'c';'mid2' = 'd'} $Hashtable
🌐
LazyAdmin
lazyadmin.nl › home › powershell hashtable – everything you need to know
PowerShell HashTable - Everything you need to know — LazyAdmin
October 4, 2022 - As we have seen an array is just a list (collection) of items, which we can add, modify or remove from the list. The index is automatically generated when you add items to it. On the contrary, with a PowerShell HashTable, we must define the index (keys) of the value that we want to add.
🌐
Reddit
reddit.com › r/powershell › how do i create and edit a two-dimensional hash table?
r/PowerShell on Reddit: how do I create and edit a two-dimensional hash table?
November 5, 2024 -

I've done plenty of 2-D arrays as well as 1-D hashes but now I seem to have confused myself in trying to do a 'simple' 2-D hash. Maybe I'm mistaken in thinking this counts as 2-D?

MY GOAL
I am trying to create a hash, e.g. for a list of foods, where each food-item (row) would contain multiple properties (columns), e.g.

Food     Calories   Fat      Price
-----     --------   -----    -----
ham      100         10       1.00
eggs     200         20       2.00

How do I initialize this hash, how do I add new rows, and how do I recall a particular row (by Food label) and edit the associated values?

Thank you for any suggestions!

🌐
Reddit
reddit.com › r/powershell › hashtable syntax
r/PowerShell on Reddit: Hashtable syntax
November 22, 2024 -

why is it when i declare as hashtable, I can access its properties like an object?

PS C:\Users\john> $obj = @{
>>     Name = "John"
>>     Age = 30
>> }
PS C:\Users\john> $obj.Name
John

is this just syntactical sugar, or something? thought i would have to do this:

$obj[Name]
🌐
MOBZystems
mobzystems.com › blog › comparing-hashtables-in-powershell
Comparing hashtables in Powershell - MOBZystems
May 18, 2022 - The MOBZystems, Home of Tools blog. Far from complete, sometimes interesting.
Find elsewhere
🌐
Evotec
evotec.xyz › home › blog › powershell – few tricks about hashtables and arrays i wish i knew when i started
PowerShell – Few tricks about HashTables and Arrays I wish I knew when I started | Evotec Blog
February 18, 2019 - As you can see above, I've created a rather simple HashTable with some values, but when I display it all the names are out of order. For storage purposes, the order doesn't matter, and when we ask HashTable to provide **Name **or Surname, it will be returned without any problems.
🌐
SS64
ss64.com › ps › syntax-hash-tables.html
PowerShell Hash Tables
A hash table is a single PowerShell object, to sort, filter or work with the pipeline you can unwrap this object into it’s individual elements with the GetEnumerator() method. ... When unwrapped with GetEnumerator, the hash table returns a collection of (DictionaryEntry) objects, so they ...
Top answer
1 of 2
5

You already got a helpful answer for the first part of your question.

This is my try at the second part - how to assign members of nested hash tables. There isn't an easy built-in syntax to set nested values while creating any not-yet-existing parent hash tables, so I've created a reusable function Set-TreeValue for that purpose.

function Set-TreeValue( $HashTable, [String] $Path, $Value, [String] $PathSeparator = '\.' ) {

    # To detect errors like trying to set child of value-type leafs.
    Set-StrictMode -Version 3.0  

    do {
        # Split into root key and path remainder (", 2" -> split into max. 2 parts)
        $key, $Path = $Path -split $PathSeparator, 2

        if( $Path ) {
            # We have multiple path components, so we may have to create nested hash table.
            if( -not $HashTable.Contains( $key ) ) {
                $HashTable[ $key ] = [ordered] @{}
            }
            # Enter sub tree. 
            $HashTable = $HashTable[ $key ]
        }
        else {
            # We have arrived at the leaf -> set its value
            $HashTable[ $key ] = $Value
        }
    }
    while( $Path )
}

Demo:

$ht = [ordered] @{}

Set-TreeValue $ht foo.bar.baz 42   # Create new value and any non-existing parents
Set-TreeValue $ht foo.bar.baz 8    # Update existing value
Set-TreeValue $ht foo.bar.bam 23   # Add another leaf
Set-TreeValue $ht fop 4            # Set a leaf at root level
#Set-TreeValue $ht fop.zop 16      # Outputs an error, because .fop is a leaf
Set-TreeValue $ht 'foo bar' 15     # Use a path that contains spaces

$ht | ConvertTo-Json -Depth 99     # Output the content of the hash table

Output:

{
  "foo": {
    "bar": {
      "baz": 8,
      "bam": 23
    }
  },
  "fop": 4,
  "foo bar": 15
}

NOTE: I've opted to create nested hash tables as OrderedDictionary as these are much more useful than regular ones (e. g. to ensure an order in a JSON output). Remove [ordered] if you want unordered hash tables (which propably have slight performance advantage).

2 of 2
2

Use the index operator to reference a specific entry by key and then assign a new value to that entry:

$hashtable = @{}

# this will add a new key/value entry
$hashtable['abc'] = 1

# this will overwrite the existing value associated with the key `abc`
$hashtable['abc'] = 2

If you have a large code base with many existing calls to .Add($key, $value) and would like to avoid refactoring every call, you can modify the behavior of the hashtable itself so that Add acts like the indexer:

function New-NonStrictHashTable {
  return @{} |Add-Member -MemberType ScriptMethod -Name Add -Value {
    param($key,$value)

    $this[$key] = $value
  } -Force -PassThru
}

Now you can do:

# Create hashtable with Add() overriden
$hashtable = New-NonStrictHashTable

$key,$value = 'key','value'

# This works like before
$hashtable.Add($key, $value)

# This works too now, it simply updates the existing entry
$hashtable.Add($key, 'some other value')

This will work for any PowerShell script statement that calls $hashtable.Add() because resolution of ETS methods (like the one we attached to the hashtable with Add-Member) takes precedence over the underlying .NET method.


Another issue with hashes that I have is this:

  • Assume you have a hashtabel $motherOfAll which will eventually contain other hashtables, which in turn will also contain hashtables.
  • Now you want to insert something into the bottommost layer of hashtables. You first need to check, that all the hashtables along the way exist and contain the proper keys.
  • If not, you have to insert a bunch of empty hashtables, which get filled with another empty one... not ad infinitum of course, but still ugly. More messy code. Is there a better way?

The desired behavior you describe here is found in Perl and is known as autovivification:

my %users;

# the nested hashes $users{YeOldHinnerk} and $users{YeOldHinnerk}{contact_details} 
# will automatically come into existence when this assignment is evaluated
$users{YeOldHinnerk}{contact_details}{email_address} = "[email protected]"

The Wikipedia article linked above gives an example of how to implement similar behavior in C#, which can be adapted for PowerShell as follows:

Add-Type -TypeDefinition @'
using System.Collections.Generic;

public class AVD
{
    private Dictionary<string, object> _data = new Dictionary<string, object>();

    public object this[string key]
    {
        get {
            if(!_data.ContainsKey(key)){
                _data[key] = new AVD();
            }
            return _data[key];
        }
        set {
            _data[key] = value;
        }
    }
}
'@

Now we can take advantage of PowerShell's native index access syntax:

PS ~> $autovivifyingHashtable = [AVD]::new()
PS ~> $autovivifyingHashtable['a']['b']['c'] = 123
PS ~> $autovivifyingHashtable['a'] -is [AVD]
True
PS ~> $autovivifyingHashtable['a']['b'] -is [AVD]
True
PS ~> $autovivifyingHashtable['a']['b']['c']
123
🌐
Reddit
reddit.com › r/powershell › everything you wanted to know about hashtables
r/PowerShell on Reddit: Everything you wanted to know about hashtables
November 6, 2016 - Great article! My preference is to use the term braces instead of 'squiggly brackets'. Also, might be worth mentioning that hashtables can be cast to a [pscustomobject] making it easy to create pscustomobjects.
🌐
SharePoint Diary
sharepointdiary.com › sharepoint diary › powershell › powershell tutorials › hash tables in powershell: a comprehensive guide
Hash Tables in PowerShell: A Comprehensive Guide - SharePoint Diary
October 2, 2025 - To convert a string to a Hashtable in PowerShell, you can use the ConvertFrom-StringData cmdlet. This cmdlet converts a string that contains one or more key and value pairs into a Hashtable. The line break separates each key-value pair.
🌐
TutorialsPoint
tutorialspoint.com › powershell › powershell_hashtables.htm
Powershell - Hashtables
This tutorial introduces how to declare hashtable variables, create hashtables, and process hashtable using its methods.
Top answer
1 of 3
3

I think what you want is something more like this:

$colors = @("black","white","yellow","blue")
$Applications=@{}
Foreach ($i in $colors)
{
    $Applications[$i] = @{
        Colour = $i
        Prod = 'SrvProd05'
        QA   = 'SrvQA02'
        Dev  = 'SrvDev12'
    }
}

I will also point out that Hashtables often need to be handled defensively. Each key must be unique but values do not need to be. Here is the typical method of handling that:

$colors = @("black","white","yellow","blue")
$Applications=@{}
Foreach ($i in $colors)
{
    if($Applications.ContainsKey($i)){
        #Do things here if there is already an entry for this key
    }else{
        $Applications[$i] = @{
            Colour = $i
            Prod = 'SrvProd05'
            QA   = 'SrvQA02'
            Dev  = 'SrvDev12'
        }
    }
}
2 of 3
3

EBGreen's helpful answer offers a solution for what you likely meant to do.

To complement that with an explanation of why your code failed:

  • When you use + to "add" two hashtables, their entries are merged: in other words: the entries of the RHS are added to the LHS hashtable.
    (Technically, a new instance is created with the merged entries.)

  • However - by sensible design - merging is only performed if the hashtables have no keys in common; otherwise, you'll get the error message you saw, complaining about duplicate keys.
    If this safeguard weren't in place, you would lose data if the values associated with duplicate entries differ.

Since your loop repeatedly tried to merge a hashtable with the same keys directly into an existing hashtable, your 2nd loop iteration invariably failed.

You can verify this more simply:

$Applications = @{}  # create empty hashtable.

# Merge a hashtable literal into $Applications.
# This works fine, because the two hashtables have no keys in common.
$Applications += @{ first = 1; second = 2 } 

# $Application now contains the following: @{ first = 1; second = 2 }

# If you now try to add a hashtable with the same set of keys again,
# the operation invariably fails due to duplicate keys.
$Applications += @{ first = 10; second = 20 } # FAILS

# By contrast, adding a hashtable with unique keys works fine:
$Applications += @{ third = 3; fourth = 4 } # OK

# $Application now contains: @{ first = 1; second = 2; third = 3; fourth = 4 }
Top answer
1 of 4
17

There's good information in the existing answers, but given your question's generic title, let me try a systematic overview:

  • You do not need to convert a hashtable to a [pscustomobject] instance in order to use dot notation to drill down into its entries (properties), as discussed in the comments and demonstrated in iRon's answer.

    • A simple example:

      @{ top = @{ nested = 'foo' } }.top.nested  # -> 'foo'
      
    • See this answer for more information.

  • In fact, when possible, use of hashtables is preferable to [pscustomobject]s, because:

    • they are lighter-weight than [pscustomobject] instances (use less memory)
    • it is easier to construct them iteratively and add / remove entries on demand.

Note:

  • The above doesn't just apply to the [hashtable] type, but more generally to instances of types that implement the [System.Collections.IDictionary] interface or its generic counterpart, System.Collections.Generic.IDictionary[TKey, TValue]], notably including ordered hashtables (which are instances of type System.Collections.Specialized.OrderedDictionary, which PowerShell allows you to construct with syntactic sugar [ordered] @{ ... }).

  • Unless noted, hashtable in the following section refers to all such types.


In cases where you do need to convert a [hasthable] to a [pscustomobject]:

While many standard cmdlets accept [hasthable]s interchangeably with [pscustomobjects]s, some do not, notably ConvertTo-Csv and Export-Csv (see GitHub issue #10999 for a feature request to change that); in such cases, conversion to [pscustomobject] is a must.

Caveat: Hasthables can have keys of any type, whereas conversion to [pscustomobject] invariably requires using string "keys", i.e. property names. Thus, not all hashtables can be faithfully or meaningfully converted to [pscustomobject]s.

  • Converting non-nested hashtables to [pscustomobject]:

    • The syntactic sugar PowerShell offers for [pscustomobject] literals (e.g., [pscustomobject] @{ foo = 'bar'; baz = 42 }) also works via preexisting hash; e.g.:

      $hash = @{ foo = 'bar'; baz = 42 } 
      $custObj = [pscustomobject] $hash   # Simply cast to [pscustomobject]
      
  • Converting nested hashtables, i.e. an object graph, to a [pscustomobject] graph:

    • A simple, though limited and potentially expensive solution is the one shown in your own answer: Convert the hashtable to JSON with ConvertTo-Json, then reconvert the resulting JSON into a [pscustomobject] graph with ConvertFrom-Json.

      • Performance aside, the fundamental limitation of this approach is that type fidelity may be lost, given that JSON supports only a few data types. While not a concern with a hashtable read via Import-PowerShellDataFile, a given hashtable may contain instances of types that have no meaningful representation in JSON.
    • You can overcome this limitation with a custom conversion function, ConvertFrom-HashTable (source code below); e.g. (inspect the result with Format-Custom -InputObject $custObj):

      $hash = @{ foo = 'bar'; baz = @{ quux = 42 } } # nested hashtable
      $custObj = $hash | ConvertFrom-HashTable # convert to [pscustomobject] graph
      

ConvertFrom-HashTable source code:

Note: Despite the name, the function generally supports instance of types that implement IDictionary as input.

function ConvertFrom-HashTable {
  param(
    [Parameter(Mandatory, ValueFromPipeline)]
    [System.Collections.IDictionary] $HashTable
  )
  process {
    $oht = [ordered] @{} # Aux. ordered hashtable for collecting property values.
    foreach ($entry in $HashTable.GetEnumerator()) {
      if ($entry.Value -is [System.Collections.IDictionary]) { # Nested dictionary? Recurse.
        $oht[[object] $entry.Key] = ConvertFrom-HashTable -HashTable $entry.Value # NOTE: Casting to [object] prevents problems with *numeric* hashtable keys.
      } else { # Copy value as-is.
        $oht[[object] $entry.Key] = $entry.Value
      }
    }
    [pscustomobject] $oht # Convert to [pscustomobject] and output.
  }
}
2 of 4
3

What is the issue/question?

@'
@{
    AllNodes = @(
        @{
            NodeName = 'SRV1'
            Role = 'Application'
            RunCentralAdmin = $true
        },
        @{
            NodeName = 'SRV2'
            Role = 'DistributedCache'
            RunCentralAdmin = $true
        },
        @{
            NodeName = 'SRV3'
            Role = 'WebFrontEnd'
            PSDscAllowDomainUser = $true
            PSDscAllowPlainTextPassword = $true
            CertificateFolder = '\\mediasrv\Media'
        },
        @{
            NodeName = 'SRV4'
            Role = 'Search'
        },
        @{
            NodeName = '*'
            DatabaseServer = 'sql1'
            FarmConfigDatabaseName = '__FarmConfig'
            FarmContentDatabaseName = '__FarmContent'
            CentralAdministrationPort = 1234
            RunCentralAdmin = $false
        }
    );
    NonNodeData = @{
        Comment = 'No comment'
    }
}
'@ |Set-Content .\nodes.psd1
$psdnode = Import-PowerShellDataFile .\nodefile.psd1

$psdnode

Name                           Value
----                           -----
NonNodeData                    {Comment}
AllNodes                       {SRV1, SRV2, SRV3, SRV4…}
$psdnode.AllNodes.where{ $_.NodeName -eq 'SRV3' }.Role
WebFrontEnd
🌐
Microsoft Learn
learn.microsoft.com › en-us › powershell › scripting › lang-spec › chapter-10
Hashtables - PowerShell | Microsoft Learn
January 8, 2025 - The type Hashtable represents a collection of key/value pair objects that supports efficient retrieval of a value when indexed by the key.
🌐
SSOJet
ssojet.com › data-structures › implement-hash-table-in-powershell
Implement Hash Table in PowerShell | Implement Data Structures in Programming Languages
You can create a hash table in PowerShell using the literal syntax @{}, which is generally the most concise and preferred method. Alternatively, you can instantiate it using New-Object System.Collections.Hashtable.
🌐
PowerShell Forums
forums.powershell.org › powershell help
Create HashTable With Multiple Values - PowerShell Help - PowerShell Forums
April 24, 2020 - Hi, I think i need a hash table for this? I would like a script to loop through a hash table and for each line, pass the values to my function. I have a working solution for 2 columns or a key and a value i believe?: $accounts = @ item1 = 'string_value1' item2 = 'string_value2' item3 = 'string_value3' } foreach ($line in $accounts.GetEnumerator()){ Get-MyBalanceFunction -account $($line.Name) -string $($line.Value) } My issue is trying to add a second value, I would like the list to have 3...