iRon
iRon

Reputation: 23703

Anonymous Recursive Function

Is it possible to create an anonymous Recursive Function in PowerShell? (if yes, how?)

I have a recursive object and using a recursive function to drill down through the properties, like:

$Object = ConvertFrom-Json '
{
    "Name" : "Level1",
    "Folder" : {
        "Name" : "Level2",
        "Folder" : {
            Name : "Level3"
        }       
    }
}'

Function GetPath($Object) {
    $Object.Name
    if ($Object.Folder) { GetPath $Object.Folder }
}

(GetPath($Object)) -Join '\'

Level1\Level2\Level3

The function is relative small and only required ones, therefore I would like to directly invoke it as an anonymous function, some like:

(&{
    $Object.Name
    if ($Object.Folder) { ???? $Object.Folder }
}) -Join '\'

Is this possible in PowerShell? If yes, how can I (as clean as possible) refer to the current function at ?????

Upvotes: 3

Views: 183

Answers (2)

Santiago Squarzon
Santiago Squarzon

Reputation: 60145

Unfortunately there is not much documentation on this topic but you could execute the anonymous script block via $MyInvocation automatic variable, specifically it's ScriptInfo.ScriptBlock Property.

A simple example:

& {
    param([int] $i)

    if($i -eq 10) { return $i }
    ($i++)
    & $MyInvocation.MyCommand.ScriptBlock $i
}

# Results in 0..10

Using your current code and Json provided in question:

(& {
    param($s)

    $s.Name; if ($s.Folder) { & $MyInvocation.MyCommand.ScriptBlock $s.Folder }
} $Object) -join '\'

# Results in Level1\Level2\Level3

Same as the above but using pipeline processing instead:

($Object | & {
    process {
        $_.Name; if($_.Folder) { $_.Folder | & $MyInvocation.MyCommand.ScriptBlock }
    }
}) -join '\'

A bit more code but the same can be accomplished using a Collections.Queue instead of recursion, which is likely to be more resource efficient:

$(
    $queue = [System.Collections.Queue]::new()
    $queue.Enqueue($object)
    while($queue.Count) {
        $node = $queue.Dequeue()
        $node.Name
        if($node.Folder) { $queue.Enqueue($node.Folder) }
    }
) -join '\'

Upvotes: 6

iRon
iRon

Reputation: 23703

@Santiago's helpful answer was exactly where I was initially looking for.
Nevertheless, it doesn't always require a recursive function to crawl through a recursive object.
As in the mcve, I could just have done:

@(
    do {
        $Object.Name
        $Object = $Object.Folder
    } while ($Object)
) -Join '\'

Upvotes: 2

Related Questions