Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

transform hashtable to object with recursion

Tags:

powershell

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.

like image 302
Anthony Stringer Avatar asked Dec 02 '25 13:12

Anthony Stringer


1 Answers

Oh my gosh! I have been working on this for a few hours now but I think I have something that is working. I had to use Add-Member more than I would have liked but it was the way I got to create empty objects so they would not be equal to $null. That was important because that test determined with new nested object need be created.

function Add-NestedObject($sourceObject, $path, $objectData){
    # This function will add the object $objectToNest into $sourceObject into the location named by $parentPath
    $currentPath,$remainingPath = $path.Split(".",2)

    # Check to see if the object contains the following subproperty.
    if($sourceObject.$currentPath -eq $null){
        # This property does not exist and needs to be created. Use an empty object
        Add-Member -Name $currentPath -TypeName PSObject -InputObject $sourceObject -MemberType NoteProperty -Value (New-Object -TypeName PSObject)
    }

    # Are there more elements to this path?
    if($remainingPath){
        # There are more nested objects. Keep passing data until we get to the point where we can populate it.  
        Add-NestedObject ($sourceObject.$currentPath) $remainingPath $objectData
    } else {
        # Now we can use the data and populate this object.
        $props = @{} 
        $objectData | ForEach-Object{
            $_.Name = $_.Name.Split(".")[-1]
            $props.($_.Name) = $_.Value
        }

        # Set the current path in the object to contain the data we have been passing. 
        $sourceObject.$currentPath = [pscustomobject]$props
    }
}


$schema = $hash.GetEnumerator() | 
    Select-Object Name,Value,@{Name="Parent";Expression={$split = $_.Name -split "\.";$split[0..($split.Count - 2)] -join "."}} | 
    Group-Object Parent | Sort-Object Name

# Empty Object to start
$object = New-Object -TypeName PSObject 

# Build the object skeleton
$schema | ForEach-Object{Add-NestedObject $object $_.Name $_.Group}

# Show the monstrosity
$object

The basics is that we use group object to collect all of the values into there parent properties. They for each of those parent properties we create each node in the path (assuming that it does not already exist) using a recursive function. Once we create all the nodes then we can place the value collection inside that node.

The value collection is rebuilt as a custom object and assigned to the end node.

Here is what the thing looks like as JSON just so you can see what the object looks like now that it is converted.

{
    "root":  {
                 "test":  [
                              1,
                              2
                          ],
                 "Blubb":  [
                               5
                           ],
                 "controller":  {
                                    "name":  [
                                                 "10.3.3.171",
                                                 "10.3.3.172"
                                             ],
                                    "haha":  [
                                                 "hoho",
                                                 "hoho"
                                             ],
                                    "hugo":  [
                                                 12
                                             ],
                                    "test":  [
                                                 2,
                                                 2
                                             ],
                                    "upsala":  [
                                                   "handy"
                                               ],
                                    "renate":  [
                                                   123
                                               ]
                                },
                 "t":  {
                           "t1":  {
                                      "wert":  [
                                                   1
                                               ]
                                  },
                           "t2":  {
                                      "wert":  [
                                                   2
                                               ]
                                  }
                       }
             }
}
like image 126
Matt Avatar answered Dec 06 '25 03:12

Matt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!