ted
ted

Reputation: 2355

Updating hash table values in a 'foreach' loop in PowerShell

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

Answers (10)

iRon
iRon

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

Dhanasekaran M
Dhanasekaran M

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

Dhanasekaran M
Dhanasekaran M

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

Scooter
Scooter

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

ScubaZip
ScubaZip

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

Andy Peaple
Andy Peaple

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

Emiliano Poggi
Emiliano Poggi

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

tenpn
tenpn

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

Will Priestley
Will Priestley

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

Andrew Savinykh
Andrew Savinykh

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

Related Questions