You're creating a hashtable, whose entries cannot be accessed with positional indices (0, 1, ...); instead what you specify inside [...] is an entry key.

Therefore, use the .Keys property for enumeration:

$test = @{set01 = '0'; set02 = '1'}
foreach (test.Keys) {
  $key 
  # To access the entry's *value*, use $test[test[$key]
}

The result is:

set02
set01

Note how the keys were enumerated in a different order than they were defined, because [hashtable] instances do not guarantee any particular enumeration order.

In PSv3+ you can use an ordered hashtable to remedy that, which would also allow you to use positional indexing - but note that positional indexing then returns the entry values, not the keys:

$test = [ordered] @{set01 = '0'; set02 = '1'}
for (i -lt $test.Count; $i++) {
  $test[$i]
}

Note: While you could use $test.Keys[$i] to access the keys, as in Dandre's answer, direct enumeration of the keys with foreach (test.Keys) is less verbose and faster; the only good reason to use index-based iteration with $test.Keys[$i] would be if index $i weren't just a helper variable, but were required for operation, such as for outputting a string that reflects both a key and its index.

The above therefore yields:

0
1

Note that the for syntax is not only verbose, it actually performs worse than foreach, and in case you do need explicit indices, use foreach with the indices generated via the range operator (..), which is not only faster but also arguably more readable than the for loop:

$test = [ordered] @{set01 = '0'; set02 = '1'}
foreach (test.Count-1)) {
   $test[$i]
}

While it is less memory-efficient, because the array of indices must be created as a whole up front, that is usually not a concern.

Answer from mklement0 on Stack Overflow
🌐
PowerShell Forums
forums.powershell.org › powershell help
Hash Tables GetEnumerator method - PowerShell Help - PowerShell Forums
June 11, 2014 - #Instead of posting a large hash table the following generates a small one $hash = @{} for ($i=1; $i -le 20; $i++) { $date = (get-date).AddDays((Get-Random -Minimum 1 -Maximum 365)); $delay = (Get-Random -Minimum 1 -Maximum 30) $hash[$i] = @{delay = $delay; Date = $date} } #Working with the hash table $var = $hash.GetEnumerator() $var #displays the rows of the hash table $var #empty?
🌐
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
We are walking each key in the hashtable and then using it to access the value. This is a common pattern when working with hashtables as a collection. That brings us to GetEnumerator() for iterating over our hashtable.
Discussions

Why isn't .GetEnumerator() treated as array by default on hashtables/dictionaries?
Why isn't .GetEnumerator() treated as array by default Because the intention of the method is not to return an array. This article will provide some context. The GetEnumerator() method returns an enumerator (in the case of hashtables, [HashtableEnumerator] for unordered and [OrderedDictionaryEnumerator] for ordered). $test.GetEnumerator().GetType() An enumerator is not the same as an enumeration. It is a scalar object that implements an interface (IEnumerator) which allows iteration to be performed via the MoveNext() method. The enumerator allows work to be deferred until actually needed, as opposed to collecting the full results up front. $test.GetEnumerator() yields the same result as ($test.GetEnumerator())[0], but only because PowerShell allows indexing of scalar objects. This is done to abstract away the complexity of differentiating between scalars and collections. In either case, the result is the enumerator object itself. Using the array subexpression operator (@()) forces enumeration and collects the full results in an array (in this case, all key/value pairs of the hashtable), which can then be indexed like usual with array/list collection types. More on reddit.com
🌐 r/PowerShell
3
5
November 20, 2023
powershell - How can I enumerate a hashtable as key-value pairs / filter a hashtable by a collection of key values - Stack Overflow
Editor's note: This question has a complicated history, but boils down to this: * To learn how to enumerate the entries of a hashtable by its key-value pairs, see the accepted answer. * To learn ho... More on stackoverflow.com
🌐 stackoverflow.com
dictionary - Looping through a hash, or using an array in PowerShell - Stack Overflow
This approach does not work if hashtable contains key = 'Keys'. 2019-03-19T06:35:25.183Z+00:00 ... Save this answer. ... Show activity on this post. ... This gets me a " ForEach-Object : Cannot bind parameter 'Process'. Cannot convert the "getEnumerator" value of type "System.String" to type ... More on stackoverflow.com
🌐 stackoverflow.com
powershell - Why I can use both `Name` and `Key` to get the key in `$hashTable.GetEnumerator()`? - Stack Overflow
Recently, I've been using a hashtable in PowerShell and getting the keys and values of it. ... I understand the disorganization of a hashtable. And there is no property Names as the doc. ... My question is, why the header is Name-Value but not Key-Value, I think it should be consistent with its original hashtable's properties. ... $hashTable.GetEnumerator... More on stackoverflow.com
🌐 stackoverflow.com
Top answer
1 of 2
4

You're creating a hashtable, whose entries cannot be accessed with positional indices (0, 1, ...); instead what you specify inside [...] is an entry key.

Therefore, use the .Keys property for enumeration:

$test = @{set01 = '0'; set02 = '1'}
foreach (test.Keys) {
  $key 
  # To access the entry's *value*, use $test[test[$key]
}

The result is:

set02
set01

Note how the keys were enumerated in a different order than they were defined, because [hashtable] instances do not guarantee any particular enumeration order.

In PSv3+ you can use an ordered hashtable to remedy that, which would also allow you to use positional indexing - but note that positional indexing then returns the entry values, not the keys:

$test = [ordered] @{set01 = '0'; set02 = '1'}
for (i -lt $test.Count; $i++) {
  $test[$i]
}

Note: While you could use $test.Keys[$i] to access the keys, as in Dandre's answer, direct enumeration of the keys with foreach (test.Keys) is less verbose and faster; the only good reason to use index-based iteration with $test.Keys[$i] would be if index $i weren't just a helper variable, but were required for operation, such as for outputting a string that reflects both a key and its index.

The above therefore yields:

0
1

Note that the for syntax is not only verbose, it actually performs worse than foreach, and in case you do need explicit indices, use foreach with the indices generated via the range operator (..), which is not only faster but also arguably more readable than the for loop:

$test = [ordered] @{set01 = '0'; set02 = '1'}
foreach (test.Count-1)) {
   $test[$i]
}

While it is less memory-efficient, because the array of indices must be created as a whole up front, that is usually not a concern.

2 of 2
3

Agreeing with @mklement0. If you want the traditional for-loop you could do the following using the Keys property.

$test = @{set01 = '0'; set02 = '1'}

for (i -lt $test.Count; $i++) {
    Write-Host $test.Keys[$i]
}

This will output: set02 set01

🌐
Reddit
reddit.com › r/powershell › why isn't .getenumerator() treated as array by default on hashtables/dictionaries?
r/PowerShell on Reddit: Why isn't .GetEnumerator() treated as array by default on hashtables/dictionaries?
November 20, 2023 -

Just wondering why the GetEnumerator() method isn't treated as an array by default.

If it is forced using @() you can access indexes as any other array or if you use Select-Object, but trying against the raw output results in the entire hashtable/dictionary being returned:

10:09:09 PS C:\> $test = [ordered]@{"prop1" = "val1" ; "prop2" = "val"}

10:09:14 PS C:\> ($test.GetEnumerator())[0]

Name Value

---- -----

prop1 val1

prop2 val

10:09:25 PS C:\> @($test.GetEnumerator())[0]

Name Value

---- -----

prop1 val1

10:09:27 PS C:\> $test.GetEnumerator() | Select-Object -First 1

Name Value

---- -----

prop1 val1

10:09:36 PS C:\>

Anyone know why this is?

Top answer
1 of 3
12
Why isn't .GetEnumerator() treated as array by default Because the intention of the method is not to return an array. This article will provide some context. The GetEnumerator() method returns an enumerator (in the case of hashtables, [HashtableEnumerator] for unordered and [OrderedDictionaryEnumerator] for ordered). $test.GetEnumerator().GetType() An enumerator is not the same as an enumeration. It is a scalar object that implements an interface (IEnumerator) which allows iteration to be performed via the MoveNext() method. The enumerator allows work to be deferred until actually needed, as opposed to collecting the full results up front. $test.GetEnumerator() yields the same result as ($test.GetEnumerator())[0], but only because PowerShell allows indexing of scalar objects. This is done to abstract away the complexity of differentiating between scalars and collections. In either case, the result is the enumerator object itself. Using the array subexpression operator (@()) forces enumeration and collects the full results in an array (in this case, all key/value pairs of the hashtable), which can then be indexed like usual with array/list collection types.
2 of 3
5
An enumerator is a means to iterate through a collection, it is not the collection itself. And once it is triggered, which in your case happens because you reference the collection's first element by index, it enumerates the collection in question in one go. That's "works as designed" for an enumerator. Consider this (note the type differences in both iteration scenarios): $test = [ordered]@{"prop1" = "val1" ; "prop2" = "val2"} $myEnum = $test.GetEnumerator() $myEnum.ForEach({ Write-Host $("Name=" + $_.Key + ", Value=" + $_.Value + ", Type=" + $_.GetType().Name) # access each element after it has been evaluated by the enumerator }) Write-Host $("-" * 64) $myEnum.Reset() # required, in order to be able to iterate through this collection again using this enumerator while ($myEnum.MoveNext()) { Write-Host $("Name=" + $myEnum.Key + ", Value=" + $myEnum.Value + ", Type=" + $myEnum.GetType().Name) # access each element through the enumerator itself } Write-Host $("-" * 64) $myEnum # prints nothing, because the enumerator has been depleted (= it is positioned after the last element in the collection)
🌐
SS64
ss64.com › ps › syntax-hash-tables.html
How-to: Use Hash Tables in PowerShell
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 must be processed by cmdlets that can accept a collection, or alternatively pass the objects individually via ForEach. $hashtable.GetEnumerator() | ForEach-Object { …
🌐
ShellGeek
shellgeek.com › home › powershell › using getenumerator in powershell to read data
Using GetEnumerator in PowerShell to Read Data - ShellGeek
April 14, 2024 - GetEnumerator in PowerShell iterates through the HashTable and displays the keys and values of the HashTable.
🌐
4sysops
4sysops.com › home › articles › read all items in a powershell hash table with a loop
Read all items in a PowerShell hash table with a loop – 4sysops
July 28, 2023 - Like with everything in PowerShell, there’s more than one way to do this. My personal, favorite way is to use the GetEnumerator() method. I believe it’s simpler than looping over each string in the Keys property.
🌐
Arcane Code
arcanecode.com › 2020 › 12 › 14 › iterate-over-a-hashtable-in-powershell
Iterate Over A Hashtable in PowerShell – Arcane Code
December 14, 2020 - There you go, two simple ways in which you can iterate over a hash table in PowerShell. As I indicated, I prefer GetEnumerator because I have access to both the key and the value in a single variable within my loop.
Find elsewhere
🌐
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 - Use the “GetEnumerator” method to iterate through the key/value pairs in a hash table. Use the “Clone” method to create a copy of a hash table. Use the “Count” property to get the number of items in a hash table.
🌐
Itechguides
itechguides.com › home › technology explained › windows powershell explained › powershell hashtable ultimate guide with examples
PowerShell Hashtable Ultimate Guide with Examples - Itechguides
April 18, 2024 - The script includes the GetEnumerator Method in the hashtable. Then pipes the output to the Select-Object command. Finally, I pipe the output of the Select-Object command to the Export-Csv command. To read more about how to use Export-Csv Cmdlet, read our guide – Powershell NoTypeInformation: Applications and Examples.
🌐
Collecting Wisdom
collectingwisdom.com › home › powershell: how to iterate over a hash table
PowerShell: How to Iterate Over a Hash Table - Collecting Wisdom
March 14, 2024 - You can use the following basic syntax to iterate over a hash table in PowerShell: foreach ($h in $my_hashtable.GetEnumerator()) { Write-Host "$($h.Name): $($h.Value)" } This particular example iterates over each element of the hash table named ...
Top answer
1 of 2
67

You have some options here.

Enumerating through keys:

foreach ($key in $var.Keys) {
    $value = $var[$key]
    # or
    $value = $var.$key 
}

Enumerating key-value pairs (which you've discovered, but may not be using effectively):

foreach ($kvp in $var.GetEnumerator()) {
    $key = $kvp.Key
    $val = $kvp.Value
}
2 of 2
16

To complement briantist's helpful answer by focusing on filtering a hashtable by an array of key values (PSv3+ syntax):

# Sample hashtable.
$ht = @{ one = 1; two = 2; three = 3 }

# Filter it by an array of key values; applying .GetEnumerator() yields an array
# of [System.Collections.DictionaryEntry] instances, which have
# a .Key property and a .Value property.
$ht.GetEnumerator()  | ? Key -in 'one', 'two'

# Similarly, the *output* - even though it *looks* like a hashtable - 
# is a regular PS *array* ([Object[]]) containing [System.Collections.DictionaryEntry]
# entries (2 in this case).
$arrFilteredEntries = $ht.GetEnumerator()  | ? Key -in 'one', 'two'
$arrFilteredEntries.GetType().Name # -> Object[]

To further process the matching key-value pairs, simply pipe to % (ForEach-Object) and access $_.Key and $_.Value (value):

$ht.GetEnumerator()  | ? Key -in 'one', 'two' | 
  % { "Value for key '$($_.Key)': $($_.Value)" }

The equivalent command using a more efficient foreach loop instead of the pipeline:

foreach ($key in $ht.Keys) { 
  if ($key -in 'one', 'two') { "Value for key '$($key)': $($ht.$key)" }
}

Note: In PSv2:
* operator -in is not supported, but you can use -contains instead with the operands swapped:
'one', 'two' -contains $key
* in the pipeline, use Where-Object { 'one', 'two' -contains $_.Key }

With the sample hashtable, this yields:

Value for key 'two': 2
Value for key 'one': 1

Note how the key order in the output differs from the definition order; in PSv3+, you can create ordered hashtables ([ordered] @{ ... }) to preserve the definition order.

The key-filtering technique used above is not limited to filtering by literal key arrays; any (string) collection will do as the RHS of the -in operand, such as the .Keys collection of a different hashtable:

# Sample input hashtable.
$htInput = @{ one = 1; two = 2; three = 3 }

# Hashtable by whose keys the input hashtable should be filtered.
# Note that the entries' *values* are irrelevant here.
$htFilterKeys = @{ one = $null; two = $null }

# Perform filtering.
$htInput.GetEnumerator()  | ? Key -in $htFilterKeys.Keys | 
  % { "Value for key '$($_.Key)': $($_.Value)" }

# `foreach` loop equivalent:
foreach ($key in $htInput.Keys) {
  if ($key -in $htFilterKeys.Keys) { "Value for key '$($key)': $($htInput.$key)" }
}

The result is the same as in the example with the static filter-keys array.

Finally, if you want to filter a hashtable in place or create a new hashtable with only the filtered entries:

# *In-place* Updating of the hashtable.
# Remove entries other than the ones matching the specified keys.
# Note: The @(...) around $ht.Keys is needed to clone the keys collection before
# enumeration, so that you don't get an error about modifying a collection
# while it is being enumerated.
foreach ($key in @($ht.Keys)) { 
  if ($key -notin 'one', 'two') { $ht.Remove($key) } 
} 

# Create a *new* hashtable with only the filtered entries.
# By accessing the original's .Keys collection, the need for @(...) is obviated.
$htNew = $ht.Clone()
foreach ($key in $ht.Keys) { 
  if ($key -notin 'one', 'two') { $htNew.Remove($key) }
} 

As an aside:

The default output format for [System.Collections.DictionaryEntry] (and thus hashtables ([System.Collections.Hashtable]) uses column name Name rather than Key; Name is defined as an alias property of Key added by PowerShell (it is not part of the [System.Collections.DictionaryEntry].NET type definition; verify with
@{ one = 1 }.GetEnumerator() | Get-Member).

🌐
Local Host
locall.host › home › powershell › unlocking the secrets of powershell hash tables: a comprehensive guide
Unlocking the Secrets of PowerShell Hash Tables: A Comprehensive Guide
August 10, 2023 - Iterating over the contents of a PowerShell hash table is a crucial aspect of working with this data structure. Fortunately, PowerShell provides two straightforward methods to accomplish this: a. *Iterating Over Items*: To loop through the key-value pairs in a hash table, use the `GetEnumerator()` method and a `foreach` loop:
🌐
Adam the Automator
adamtheautomator.com › powershell-hashtable
A Beginner Guide to Using PowerShell Hashtable
January 11, 2023 - # Get an object that can be used to iterate over the elements of a collection. $enumerator = $hashtable1.GetEnumerator() foreach ($element in $enumerator) { # Outputs a string that includes the key and value of each element in the collection.
Top answer
1 of 8
273

Shorthand is not preferred for scripts; it is less readable. The %{} operator is considered shorthand. Here's how it should be done in a script for readability and reusability:

Variable Setup

CopyPS> $hash = @{
    a = 1
    b = 2
    c = 3
}
PS> $hash

Name                           Value
----                           -----
c                              3
b                              2
a                              1

Option 1: GetEnumerator()

Note: personal preference; syntax is easier to read

The GetEnumerator() method would be done as shown:

Copyforeach ($h in $hash.GetEnumerator()) {
    Write-Host "$($h.Name): $($h.Value)"
}

Output:

Copyc: 3
b: 2
a: 1

Option 2: Keys

The Keys method would be done as shown:

Copyforeach ($h in $hash.Keys) {
    Write-Host "${h}: $($hash.$h)"
}

Output:

Copyc: 3
b: 2
a: 1

Additional information

Be careful sorting your hashtable...

Sort-Object may change it to an array:

CopyPS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object


PS> $hash = $hash.GetEnumerator() | Sort-Object Name
PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

This and other PowerShell looping are available on my blog.

2 of 8
118

Christian's answer works well and shows how you can loop through each hash table item using the GetEnumerator method. You can also loop through using the keys property. Here is an example how:

Copy$hash = @{
    a = 1
    b = 2
    c = 3
}
$hash.Keys | % { "key = $_ , value = " + $hash.Item($_) }

Output:

Copykey = c , value = 3
key = a , value = 1
key = b , value = 2
🌐
Hosting Ultra So
hostingultraso.com › help › windows › sort-hashtable-key-value-windows-powershell
Sort a Hashtable by Key or Value in Windows PowerShell | Windows PowerShell, Windows Server | HostingUltraso.com
Problem You have a hashtable of keys and values, and want to get the list of values that result from sorting the keys in order. Solution To sort a hashtable, use the GetEnumerator() method on the hashtable to gain access to its individual elements. Then use the SortObject cmdlet to sort by ...
🌐
Microsoft Learn
learn.microsoft.com › en-us › dotnet › api › system.collections.hashtable.getenumerator
Hashtable.GetEnumerator Method (System.Collections) | Microsoft Learn
Returns an IDictionaryEnumerator that iterates through the Hashtable. public: virtual System::Collections::IDictionaryEnumerator ^ GetEnumerator();
🌐
LazyAdmin
lazyadmin.nl › home › powershell hashtable – everything you need to know
PowerShell HashTable - Everything you need to know — LazyAdmin
October 4, 2022 - To actually export the contents of the hashtable you will need to use the GetEnumerator method, just like we did with viewing the contents of the hashtable.
Top answer
1 of 1
3

PowerShell allows you to define what is known as Alias Property for specified types or instances.

Name happens to be an Alias Property added to the DictionaryEntry instances:

$getMemberSplat = @{
    InputObject = [System.Collections.DictionaryEntry]::new('foo', 'bar')
    MemberType  = 'AliasProperty'
}

Get-Member @getMemberSplat

# Outputs:
#
#   TypeName: System.Collections.DictionaryEntry
#
# Name MemberType    Definition
# ---- ----------    ----------
# Name AliasProperty Name = Key

We can see that the property is an extended member of DictionaryEntry using Get-TypeData:

(Get-TypeData System.Collections.DictionaryEntry).Members['Name']

# ReferencedMemberName MemberType IsHidden Name
# -------------------- ---------- -------- ----
# Key                                False Name

Alias Properties can be added to a specified type using Update-TypeData, for example:

$updateTypeDataSplat = @{
    TypeName   = 'System.IO.DirectoryInfo'
    MemberType = 'AliasProperty'
    MemberName = 'AbsolutePath'
    Value      = 'FullName'
}

Update-TypeData @updateTypeDataSplat

# Now using the new Alias Property:
(Get-Item $pwd).AbsolutePath
# Should be the same as:
(Get-Item $pwd).FullName

They can also be added to instances of a type with Add-Member, for example:

$myObject = [pscustomobject]@{
    MyProperty = 'hi'
}

$addMemberSplat = @{
    InputObject = $myObject
    MemberType  = 'AliasProperty'
    Name        = 'MyAliasProperty'
    Value       = 'MyProperty'
}

Add-Member @addMemberSplat

$myObject.MyAliasProperty # hi

And, for custom types, can be defined through a Types.ps1xml file, the linked PowerShell doc has a mention on the built-in type system and the built-in defined aliases:

For example, by default, array objects (System.Array) have a Length property that lists the number of objects in the array. However, because the name Length does not clearly describe the property, PowerShell adds an alias property named Count that displays the same value.


Worth noting here that .Count should've never been an Alias Property in PowerShell 5.1 since .Count is an array's Explicit Interface implementation. In PowerShell 7+ we no longer see .Count as an Alias Property but as just Property:

# PowerShell 7.4
PS ..\pwsh> Get-Member -InputObject @() -Name Count

   TypeName: System.Object[]

Name  MemberType Definition
----  ---------- ----------
Count Property   int Count {get;}

# Windows PowerShell 5.1
PS ..\pwsh> powershell 'Get-Member -InputObject @() -Name Count'


   TypeName: System.Object[]      

Name  MemberType    Definition    
----  ----------    ----------    
Count AliasProperty Count = Length

Ideally, in my opinion, we should the definition as int ICollection.Count {get;}.


As for, why do you see Name and Value instead of Key and Value, this is defined by the built-in format files, in this case, the one that targets the DictionaryEntry type. You can see this by yourself:

Get-FormatData System.Collections.DictionaryEntry |
    Export-FormatData -Path DictionaryEntry.format.ps1xml

Get-Content DictionaryEntry.format.ps1xml

Fragment below is an extract of the format.ps1xml for DictionaryEntry. See about_Format.ps1xml for more details.

    <View>
      <Name>Dictionary</Name>
      <ViewSelectedBy>
        <TypeName>System.Collections.DictionaryEntry</TypeName>
      </ViewSelectedBy>
      <TableControl>
        <TableHeaders>
          <TableColumnHeader>
            <Width>30</Width>
          </TableColumnHeader>
          <TableColumnHeader />
        </TableHeaders>
        <TableRowEntries>
          <TableRowEntry>
            <TableColumnItems>
              <TableColumnItem>
                <PropertyName>Name</PropertyName>
              </TableColumnItem>
              <TableColumnItem>
                <PropertyName>Value</PropertyName>
              </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
Top answer
1 of 2
6

Note: This answer applies equally to situations where an API directly returns an enumerator object.

Using .GetEnumerator() returns an enumerator[1] for the key-value pairs, which is not the same as the results of the enumeration.

  • An enumerator must be iterated over via its .MoveNext() method in order to perform actual enumeration. However, you can let PowerShell do this for you, as shown below.

To get the desired behavior, force enumeration via @(...), the array sub-expression operator, and use the results:

# Note the use of @(...), which collects the enumerated objects
# in an [object[]] array.

# Get an array of key-value pairs.
$KeyPairs = @($ht.GetEnumerator())

# Get the first key-value pair.
@($ht.GetEnumerator())[0]

Note:

  • In the pipeline it is PowerShell itself that performs the enumeration of an enumerator object, which is why something like
    $ht.GetEnumerator() | ForEach-Object { <# work with each key-value pair #> } does work.

  • In the pipeline, hashtables / dictionaries, which are technically also collections, are not enumerated by default, unlike list-like collections such as arrays.[2] That is, hashtables / dictionaries are sent as a whole through the pipeline by default, which is why a call to .GetEnumerator() is needed to return an enumerator for their entries (key-value pairs), which the pipeline then enumerates.


As for what you tried:

$KeyPairs
# Nothing is returned as if $KeyPairs lost its value

Because $KeyPairs contains an enumerator, it is done enumerating after the first enumeration that output to the pipeline (the display) implicitly performed, and therefore there's nothing left to enumerate on re-invocation - unless you call $KeyPairs.Reset() first.
However, note that not every enumerator is guaranteed to support .Reset() for repeating an enumeration - some enumerators invariably perform one-time-only enumerations.

($ht.GetEnumerator())[0] # !! DOESN'T WORK
  • An enumerator cannot be indexed into.

  • PowerShell treats it like a single object (which it is) and falls back to its own indexing, where it allows even single objects (scalars) to be indexed for the sake of unified handling of collections and scalars; in that case, [0] is an effective no-op, simply returning the single object itself (similar to how (42)[0] and (42)[-1] are the same as 42)


[1] Specifically, .GetEnumerator() returns an object that implements the System.Collections.IDictionaryEnumerator interface.

[2] See the bottom section of this answer for which types PowerShell does and doesn't automatically enumerate in the pipeline.

2 of 2
5

$KeyPairs in your example $KeyPairs = $ht.GetEnumerator() is a misleading name, you do not have Key / Value pairs allocated in that variable, what you have is an object of the type HashtableEnumerator, a type that implements the IDictionaryEnumerator Interface, this interface, basically is a "contract" that specifies how a hash table can and should be enumerated. One of the specifics of the implementation is that the instance can be enumerated once and, if you want to enumerate it again, you must call its .Reset() method. This, as far as I know, is applicable for any type implementing the IEnumerator interace (base interface for IDictionaryEnumerator).

PS ..\pwsh> $ht = @{'Key1' = 'Value1'; 'Key2' = 'Value2' }
PS ..\pwsh> $enum = $ht.GetEnumerator()
PS ..\pwsh> $enum

Name                           Value
----                           -----
Key1                           Value1
Key2                           Value2

PS ..\pwsh> $enum.Reset()
PS ..\pwsh> $enum

Name                           Value
----                           -----
Key1                           Value1
Key2                           Value2

As for the second question:

The hashtable enumerator seems to only support the Select-Option, not the index.

That is correct, enumerators don't implement IList interface nor the implement an indexer.

If you want to use indexing you must convert that HashtableEnumerator into a collection of DictionaryEntry using @($ht.GetEnumerator()), in which case, using a hashtable in the first place wouldn't make sense, it would lose its purpose.

Or, a better approach would be to use an OrderedDictionary, this type supports accessing by index and by key and is as fast as a normal hashtable while performing look-ups:

$dict = [ordered]@{'Key1' = 'Value1'; 'Key2' = 'Value2' }
$dict[0]      # Value1
$dict.Keys[0] # Key1

# or, for a key / value pair:
[System.Collections.DictionaryEntry]::new($dict.Keys[0], $dict[0])

# Name                           Value
# ----                           -----
# Key1                           Value1