Search code examples
arraysjsonpowershellforeachpowershell-4.0

Get specific value from JSON array after comparison using PowerShell?


I want to get specific value once key is matched from JSON array object

Input:

[
    {
        "Id": "19A7A3C4",
        "displayName": "somename",
        "tags": [
            {
                "context": "CONTEXTLESS",
                "key": "apple",
                "value": "10"
            },
            {
                "context": "CONTEXTLESS",
                "key": "orange",
                "value": "20"
            },
            {
                "context": "CONTEXTLESS",
                "key": "grapes",
                "value": "30"
            }
        ]
    },
    {
        "Id": "111111",
        "displayName": "somename",
        "tags": [
            {
                "context": "CONTEXTLESS",
                "key": "cat",
                "value": "10"
            },
            {
                "context": "CONTEXTLESS",
                "key": "cat",
                "value": "20"
            }
        ]
    }
]

I want to get the value of tag where key matches to cat and value matches to 10, I am using below query but getting whole object

$content = Get-Content -Raw -Path "data.json" | ConvertFrom-Json
$content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"}

Desired Output: 10


Solution

  • Mathias's answer already shows a clean way to solve the problem.

    A similar approach is using the intrinsic method .Where{}:

    $tagValues = $content.tags.Where{ $_.key -eq "cat" -and $_.value -eq "10" }.value
    

    $content.tags employs member enumeration to collect all tags properties into an array. .Where{} filters array elements similar to Where-Object. Lastly .value uses member enumeration again to collect the filtered tag values into an array.

    Intrinsic methods like .Where{} are supposed to be faster than pipeline commands because they don't involve the overhead of the pipeline machinery.


    If you want to keep your original query, you have to deal with nested properties.

    Use grouping operator () and dot notation to extract a given property:

    $tagValues = ($content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"}).tags.value
    

    An alternative is Select-Object with parameter -ExpandProperty (alias -Expand), but it doesn't work as straightforward for nested properties (yet):

    $tagValues = $content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"} |
        Select-Object -Expand tags | Select-Object -Expand value
    

    A more straightforward alternative is ForEach-Object:

    $tagValues = $content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"} |
        ForEach-Object { $_.tags.value }