Reputation: 578
I'm trying to get the enumerator while looping a hash table.
I do understand how it works in a foreach
loop like the following:
$test = @{set01 = '0'; set02 = '1'}
foreach ($t in $test.GetEnumerator()) {
Write-Host 'key: '$t.Key
}
Which would output the individual key per loop:
key: set02
key: set01
I'd like to know if it is possible to do that in a C type loop such as:
$test = @{set01 = '0'; set02 = '1'}
for ($i = 0; $i -lt 1; $i++) {
Write-Host 'key: '$test.Keys[$i] # edited with Dandré's answer.
}
Which lists all the keys:
key: set02 set01
key:
Is that possible or does the .GetEnumerator()
only work in a foreach
situation?
edit: Reading mklement0's answer i see that it's not directly possible. I'll mark it as the answer for the detailed explanation.
One workaround i figured out to extract the key for the loop position is partially linked to Dandré's answer combined with mklement0's explanation.
If the keys are collected into an array prior to running the loop they can be looked up while running the for loop.
$test = @{set01 = '0'; set02 = '1'}
$keys = @($test.Keys)
for ($i = 0; $i -lt $test.Count; $i++) {
Write-Host 'key:'$keys[$i]
}
This will result as expected:
key: set02
key: set01
depending on the array the keys need to be reversed first [array]::Reverse($keys)
.
The $keys
variable can also be used within the loop to call specific keys as in: $test[$keys[$i]]
. It's a bit of a hack but opens some interesting possibilities.
Upvotes: 3
Views: 8066
Reputation: 437588
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 ($key in $test.Keys) {
$key
# To access the entry's *value*, use $test[$key] $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 = 0; $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 ($key in $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 ($i in 0..($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.
Upvotes: 3
Reputation: 2173
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 = 0; $i -lt $test.Count; $i++) {
Write-Host $test.Keys[$i]
}
This will output: set02 set01
Upvotes: 3