/PowerShell

Why is my PowerShell function returning two values?

Introduction

This post is regarding a small PowerShell function that I was writing. It started returning two values at the same time and it took me a while to figure out what was happening. Hence this post and hope it saves someone else some time.

The function

The objective of the function we are writing is to check if an item is present in a JSON array.

{
    [
        {
            "displayName": "Apple",
            "description": "Fruit"
        },
        {
            "displayName": "Potato",
            "description": "Vegetable"
        }
    ]
}

Our JSON array is a simple displayName & description pair elements. And we need an ItemExists function in PowerShell returning boolean value, true if the item exists and false if it doesn’t.

Here is a script with the function:

function ItemExists {
    param (
        [PSCustomObject]$jsonData,
        [string]$itemToLookFor
    )
    $jsonData | Select-Object -Property displayName | ForEach-Object {
        if ($itemToLookFor -eq $_.displayName) {
            return $true
        }
    }
    return $false
}

$json = @"
    [
    {
        "displayName": "Apple",
        "description": "Fruit"
    },
    {
        "displayName": "Potato",
        "description": "Vegetable"
    }
    ]
"@ | ConvertFrom-Json

$exists = ItemExists -jsonData $json -itemToLookFor "Apple"
Write-Host exists=$exists

As you can see, we are looking for string Apple and it exists, so it should return true. But the output is as follows:

exists=True False

Is the function returning two values? And if yes how and if no then why is this happening!

Explanation

The point of interest here is ForEach-Object. Beginning with PowerShell 7.0, it runs each script block in parallel. The first return statement is within the scope of the ForEach-Object and the second one is outside of the scope. Meaning the first return is returning from ForEach-Object which is in a separate runspace. This then leads to the next return statement. So the function is returning two values, due to the loop running in separate runspaces.

Rewriting the function

How do we get the function to work then? Let’s rewrite the function without ForEach-Object, rather we can use foreach loop, as follows.

function ItemExists {
    param (
        [PSCustomObject]$jsonData,
        [string]$itemToLookFor
    )
    $items = $jsonData | Select-Object -Property displayName
    foreach ($item in $items) {
        if ($itemToLookFor -eq $item.displayName) {
            return $true
        }
    }
    return $false
}

$json = @"
    [
    {
        "displayName": "Apple",
        "description": "Fruit"
    },
    {
        "displayName": "Potato",
        "description": "Vegetable"
    }
    ]
"@ | ConvertFrom-Json

$exists = ItemExists -jsonData $json -itemToLookFor "Apple"
Write-Host exists=$exists

This time the loop doesn’t run in separate runspaces and hence the output is:

exists=True

This is exactly the value we wanted.

Conclusion

Hope this was useful and saves you some time. Please do share your learnings. If you have any thoughts or comments please do get in touch with me on twitter @rubberduckdev. Or use the Disqus plugin below.

Dushyant

Dushyant

Check about page for details.

Read More

Why is my PowerShell function returning two values?

...