Search code examples
powershellhashtable

Powershell: get index of key in [Ordered] hashtable


I know I can add to an [Ordered] hash table at a particular index, with

$ordered.Insert([Int], [String], [String])

and I can set the value at a certain index with

$ordered[[Int]] =

And that leads me to THINK that I should also be able to GET the index of a given key. It seems that direct access isn't possible, as there in no .IndexOf() method. But I know I can get an array of the keys, and an array does have .IndexOf(), but that then returns another array, of -1 for the non matches and 0 for the matches. So

$ordered = [Ordered]@{
    'one' = 1
    'two' = 2
    'three' = 3
    'four' = 4
}
$ordered
$ordered.Keys.IndexOf('two')

produces

Name                           Value                                                                                                                                                                                                                        
----                           -----                                                                                                                                                                                                                        
one                            1                                                                                                                                                                                                                            
two                            2                                                                                                                                                                                                                            
three                          3                                                                                                                                                                                                                            
four                           4                                                                                                                                                                                                                            
-1
0
-1
-1

So now I need to get the index of that 0. So that doesn't seem to be the way to do it. I also found this. And indeed this works.

[Array]::IndexOf($ordered.Keys,'three')

But it has me wondering, is there a way to make the previous approach work? $ordered.Keys.IndexOf([String]) just seems like it should be viable, and I am just missing how to extract only the successful match. But perhaps not, and the static method is the way to go?


Solution

  • In Windows PowerShell, use @(...) to convert the .Keys collection to a regular array, on which you can call .IndexOf():

    @($ordered.Keys).IndexOf('two')
    

    Note: Unlike direct, key-based access to your ordered hashtable, .IndexOf() is case-sensitive.


    As Santiago Squarzon points out, the use of @(...) is no longer necessary in PowerShell (Core) 7.3+ (.NET 7+), where the collection type contained in the .Keys property value itself implements the IList interface and therefore directly supports .IndexOf().


    Note:

    • The above also applies to accessing .Keys by index, e.g. @($ordered.Keys)[0] / $ordered.Keys[0]

    • As of this writing, the 7.3+ / .NET 7+ improvement will only apply to System.Collections.Specialized.OrderedDictionary, the type that PowerShell's [ordered] @{ ... } literal syntax creates instances of, and not also to other ordered or sorted dictionary types; GitHub issue #63537 aims to change that.


    As for what happened in your attempt:

    Since in Windows PowerShell the .Keys collection itself has no .IndexOf() method, member-access enumeration was performed, resulting in .IndexOf() calls - as a string method - on each key.