Justin Beagley
Justin Beagley

Reputation: 299

foreach through hashtable values

I know that this question has already been answered for hashtable keys... but it does not seem to work for hashtable values.

I'm creating a hash of VM's based on the cluster they reside in. So the hashtable looks like this

$clusters[$clustername][$clustervms] = @{}

The reason each VM is a hashtable is because i'm trying to associate it with their VM tag as well (vmware).

This code works incredibly fast but destroys the keys, by injecting values as keys... or in other words, Rather than key/value pairs - values become keys, keys become values ... it's just a shit show.

foreach ($value in $($clusters.values)) { 
$clusters[$value] = (get-tagassignment -entity ($value).name).tag

This code works - but it is unbelievably slow.

foreach ($key in $($clusters.keys)) {
       $vms = (Get-Cluster -Name $key | Get-Vm).name
       foreach ($vm in $vms) {
            $clusters[$key][$vm] = @{};
            $tag = (Get-TagAssignment -Entity $vm).tag;
            $clusters[$key][$vm] = $tag;
       }
}

When i say unbelievably slow - i mean getting the VM names takes about 5 seconds. Getting the tag assignments through the first code (codename: shit show) takes about 7 seconds. I've waited a minute on this code, and it's only gone through 6 VM's in that time. So i know there's a better way.

Thanks,

Upvotes: 1

Views: 3982

Answers (1)

Jake H
Jake H

Reputation: 1740

I commented on this above, I wrote an example script which should make this more clear. Also note this powershell is meant to be illustrative, and some/many/or all things could be done in a more efficient manner.

# for example, im just using sourcedata variable to make this clearer. 
# you would normally be populating this hash programatically

# lets say a VM has this payload data:
#   @{ vm_name="bar"; os="win" }
$SourceData = @(
    @{  
        cluster_name = "foo";  
        vms = @( @{ vm_name="bar" ; os="win" }, @{ vm_name="baz"; os="linux" }) 
    }, @{  
        cluster_name = "taco"; 
        vms = @( @{ vm_name="guac"; os="win" }, @{ vm_name="hot"; os="win"   }) 
    })

$clusters = @{}

# load the sourcedata into our clusters catalog
$SourceData | %{         
    $clusternm = $_.cluster_name
    $clusters[ $clusternm ] = @{}
    $_.vms | %{ 
        $vmnm = $_.vm_name 
        $clusters[ $clusternm ][ $vmnm ] = $_
    }
}

# show the whole thing
$clusters | ConvertTo-Json | Write-Output
<#
{
    "taco":  {
                 "hot":  {
                             "os":  "win",
                             "vm_name":  "hot"
                         },
                 "guac":  {
                              "os":  "win",
                              "vm_name":  "guac"
                          }
             },
    "foo":  {
                "bar":  {
                            "os":  "win",
                            "vm_name":  "bar"
                        },
                "baz":  {
                            "os":  "linux",
                            "vm_name":  "baz"
                        }
            }
}
#>

# show just a vm
$clusters['foo']['bar']  | ConvertTo-Json | Write-Output
<# 
{
    "os":  "win",
    "vm_name":  "bar"
}
#>

And finally, to assure you that iterating hashes takes no appreciable time:

# now lets iterate each cluster, and each vm in that cluster. in this example, just dump the OS of each vm in each cluster
$clusters.Keys | %{
    $clusternm = $_
    $clusters[$clusternm].Keys | %{
        $vmnm = $_
        Write-Output "${clusternm}/${vmnm}: os: $( $clusters[$clusternm][$vmnm].os )"
    }

}
<#
taco/hot: os: win
taco/guac: os: win
foo/bar: os: win
foo/baz: os: linux
#>

Whole script runs immediately. Only the json conversion methods to have illustrative output added 0.1s

Upvotes: 1

Related Questions