3

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 ?????

4
  • 1
    @SantiagoSquarzon, beat me to it lol Commented Apr 28, 2022 at 14:27
  • my answer was focusing on your main question but now I'm wondering why not consider use a Queue for this particular case ? Commented Apr 28, 2022 at 14:45
  • 1
    @Santiago, thanks for the answer (I was murdering around as I forgot to implement the paran($s) statement). Not sure if I understand "consider use a Queue"? Do you mean to keep the function flat (not recursive) and just loop though the child folder properties until it doesn't exist anymore? 🤔, I will actually consider that... Commented Apr 28, 2022 at 14:55
  • 1
    I've updated my answer using a Queue Commented Apr 28, 2022 at 15:06

2 Answers 2

6

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 '\'
Sign up to request clarification or add additional context in comments.

Comments

2

@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 '\'

1 Comment

definitely, I did over-complicate it didn't I 😋

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.