Reputation: 2355
I'm trying to loop through a hash table and set the value of each key to 5 and PowerShell gives an error:
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
foreach($key in $myHash.keys){
$myHash[$key] = 5
}
An error occurred while enumerating through a collection:
Collection was modified; enumeration operation may not execute..
At line:1 char:8
+ foreach <<<< ($key in $myHash.keys){
+ CategoryInfo : InvalidOperation: (System.Collecti...tableEnumer
ator:HashtableEnumerator) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
What gives and how do I resolve this problem?
Upvotes: 37
Views: 87929
Reputation: 23663
You do not need to clone the whole hashtable for this example. Just enumerating the key collection by forcing it to an array @(...)
is enough:
foreach($key in @($myHash.keys)) {...
Upvotes: 9
Reputation: 111
$myHash = @{
Americas = 0;
Asia = 0;
Europe = 0;
}
$countries = @("Americas", "Asia", "Europe", "Americas", "Asia")
foreach($key in $($myHash.Keys))
{
foreach($Country in $countries)
{
if($key -eq $Country)
{
$myHash[$key] += 1
}
}
}
Updating a hash value if array elements matched with a hash key.
Upvotes: 0
Reputation: 111
$myHash = @{
Americas = 0;
Asia = 0;
Europe = 0;
}
$countries = @("Americas", "Asia", "Europe", "Americas", "Asia")
foreach($key in $($myHash.Keys))
{
foreach($Country in $countries)
{
if($key -eq $Country)
{
$myHash[$key] += 1
}
}
}
$myHash
Upvotes: 0
Reputation: 1
As mentioned in a previous answer, clone
is the way to go. I had a need to replace any null values in a hash with "Unknown" nd this one-liner does the job.
($record.Clone()).keys | %{if ($record.$_ -eq $null) {$record.$_ = "Unknown"}}
Upvotes: 0
Reputation: 31
You have to get creative!
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$keys = @()
[array] $keys = $myHash.keys
foreach($key in $keys)
{
$myHash.Set_Item($key, 5)
}
$myHash
Name Value
---- -----
c 5
a 5
b 5
Upvotes: 3
Reputation: 219
There is a much simpler way of achieving this. You cannot change the value of a hashtable while enumerating it because of the fact that it's a reference type variable. It's exactly the same story in .NET.
Use the following syntax to get around it. We are converting the keys collection into a basic array using the @()
notation. We make a copy of the keys collection, and reference that array instead which means we can now edit the hashtable.
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
foreach($key in @($myHash.keys)){
$myHash[$key] = 5
}
Upvotes: 21
Reputation: 24816
Use clone
:
foreach($key in ($myHash.clone()).keys){ $myHash[$key] = 5 }
Or in the one-liner:
$myHash = ($myHash.clone()).keys | % {} {$myHash[$_] = 5} {$myHash}
Upvotes: 5
Reputation: 4716
It seems when you update the hash table inside the foreach loop, the enumerator invalidates itself. I got around this by populating a new hash table:
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$newHash = @{}
foreach($key in $myHash.keys){
$newHash[$key] = 5
}
$myHash = $newHash
Upvotes: -1
Reputation: 39
I'm new to PowerShell, but I'm quite a fan of using in-built functions, because I find it more readable. This is how I would tackle the problem, using GetEnumerator and Clone. This approach also allows one to reference to the existing hash values ($_.value) for modifying purposes.
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$myHash.Clone().GetEnumerator() | foreach-object {$myHash.Set_Item($_.key, 5)}
Upvotes: 3
Reputation: 26280
You can't modify Hashtable while enumerating it. This is what you can do:
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$myHash = $myHash.keys | foreach{$r=@{}}{$r[$_] = 5}{$r}
Edit 1
Is this any simpler for you:
$myHash = @{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
foreach($key in $($myHash.keys)){
$myHash[$key] = 5
}
Upvotes: 56