transform hashtable to object with recursion

Viewed 1945

I've got a function that returns a hashtable and i need to transform the keys into nested objects, but i am losing my mind.

here is a hardcoded example of the hashtable I'm dealing with

# $hash = SomeFunctionThatReturnsAhashTable

$hash = @{
    'root.Blubb' = @(5)
    'root.controller.haha' = 'hoho', 'hoho'
    'root.controller.hugo' = @(12)
    'root.controller.name' = '10.3.3.171', '10.3.3.172'
    'root.controller.renate' = @(123)
    'root.controller.test' = 2, 2
    'root.controller.upsala' = @('handy')
    'root.t.t1.wert' = @(1)
    'root.t.t2.wert' = @(2)
    'root.test' = 1, 2
}

the following is the idea of what i want the hashtable to be transformed into

$obj = [pscustomobject]@{
    root = [pscustomobject]@{
        Blubb = @(5)
        controller = [pscustomobject]@{
            haha = 'hoho', 'hoho'
            hugo = @(12)
            name = '10.3.3.171', '10.3.3.172'
            renate = @(123)
            test = 2, 2
            upsala = @('handy')
        }
        t = [pscustomobject]@{
            t1 = [pscustomobject]@{
                wert = @(1)
            }
            t2 = [pscustomobject]@{
                wert = @(2)
            }
        }
        test = 1, 2
    }
}

I'm trying to split on the '.' and return sub-objects, but i don't know how to get it done. If there's a better way to go about this, please let me know. this is what i have so far.

function keytoobject ($key, $value) {
    if ($key.contains('.')) {
        [pscustomobject]@{
            ($key.substring($key.indexof('.')+1)) = (keytoobject $key.substring($key.indexof('.')+1) $value)
        }
    } else {
        [pscustomobject]@{
            $key = $value
        }
    }
}

$hash.Keys | % {
    keytoobject $_ ($hash[$_])
}

any help would be greatly appreciated.

4 Answers

here is a very simple approach for nested hashtables with recursion:

#Example hash
$obj = @{A="B";c=@{D="F";g="H"}}


# the function
function Get-HashAsObject
{
    param ([hashtable]$hash, [switch]$Deep)

    $NewHash = @{}
    foreach ($k in $hash.Keys)
    {
        if ($hash[$k] -is [hashtable] -and $Deep)
        {
            $NewHash.Add($k,(Get-HashAsObject -Deep -hash $hash[$k]))
        }
        else
        {
            $NewHash.Add($k,$hash[$k])
        }
    }
    return [PSCustomObject]$NewHash
}

"Shallow"
$s = Get-HashAsObject $obj 
$s | fc
"Deep"
$d = Get-HashAsObject $obj -Deep
$d | fc

Output:

Shallow

class PSCustomObject
{
  A = B
  c = 
    [
      class DictionaryEntry
      {
        Key = D
        Value = F
        Name = D
      }
      class DictionaryEntry
      {
        Key = g
        Value = H
        Name = g
      }
    ]

}

Deep

class PSCustomObject
{
  A = B
  c = 
    class PSCustomObject
    {
      D = F
      g = H
    }
}
# a two-liner solution to https://stackoverflow.com/questions/35947725/transform-hashtable-to-object-with-recursion

process {

    # First create the branches :
    
    $obj = $hash.keys |%{ "$_" -split "\." |% {$F=""}{(($F+=$_+".")) -replace "\.$",":"}} |
           sort-object -unique |%{ $_ -replace "[^\.]*\."," " } | ConvertFrom-Yaml

    # Then create the leaves :
    
    $hash.keys|%{ iex "`$obj.$_ = `$hash[`"$_`"]" }
}

end { 
    # just testing :
    $obj.root.controller.test
}

begin {
    $hash = @{
        'root.Blubb' = @(5)
        'root.controller.haha' = 'hoho', 'hoho'
        'root.controller.hugo' = @(12)
        'root.controller.name' = '10.3.3.171', '10.3.3.172'
        'root.controller.renate' = @(123)
        'root.controller.test' = 2, 2
        'root.controller.upsala' = @('handy')
        'root.t.t1.wert' = @(1)
        'root.t.t2.wert' = @(2)
        'root.test' = 1, 2
    }
}
Related