Home > Back-end >  Powershell: get index of key in [Ordered] hashtable
Powershell: get index of key in [Ordered] hashtable

Time:10-02

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?

CodePudding user response:

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.

CodePudding user response:

As an alternative to mklement0's helpful answer you could also extend the type itself adding a new PSScriptMethod to find index of a Key. This can be accomplished with Update-TypeData.

Update-TypeData -MemberType ScriptMethod -MemberName GetIndexOf -Value {
    param([object] $Key)

    return [array]::IndexOf($this.Keys, $Key)
} -TypeName System.Collections.Specialized.OrderedDictionary

$ordered.GetIndexOf('four') # => 3
  • Related