Home > Enterprise >  Of a list of objects select the highest minor. Patch for each major version
Of a list of objects select the highest minor. Patch for each major version

Time:01-24

Seems my brain won't function tonight. I have a list of objects that are returned from a rest API. Each object has a version property:

id                             8e038650-f7bf-11e8-8a6c-8fff434b4eff
name                           DockerInstaller
version                        {[major, 0], [minor, 209], [patch, 0], [isTest, False]}        
...

What I want to achieve is to filter this list grouped by name, and then have only 1 version per major version, the one with the highest minor.patch.

So, if the service returns:

id: some-guid
name: vsbuild
version: {[major, 0], [minor, 209], [patch, 0]} 

id: some-guid
name: vsbuild
version: {[major, 0], [minor, 200], [patch, 0]} 

id: some-guid
name: vsbuild
version: {[major, 1], [minor, 214], [patch, 0]} 

id: some-guid
name: vsbuild
version: {[major, 1], [minor, 200], [patch, 0]} 

...

I want to return:

id: some-guid
name: vsbuild
versions: @( {[major, 0], [minor, 209], [patch, 0]}, {[major, 1], [minor, 214], [patch, 0]}

...

id: some-other-guid
name: vstest
versions: @( {[major, 0], [minor, 209], [patch, 0]}, {[major, 1], [minor, 214], [patch, 0]}

Based on the answers below, in combination with my earlier ugly code I came up with this, which I can still parse with mu sleepy brain. It is easier to understand for me than the Group-Object madness.

# find all tasks with the given name
$tasks =  $taskMetadata | Where-Object { $_.name -eq $taskName }

# find all major versions for that task
$majorversions = $tasks | foreach-object { $_.version.major } | Select-Object -Unique

# find the latest version for each major version
$result = $majorversions | ForEach-Object {
    $majorversion = $_

    $tasks | 
        where-object { $_.version.major -eq $majorversion} | 
        sort-object { [version]"$($_.version.major).$($_.version.minor).$($_.version.patch)" } | 
        select-object -last 1
}

return $result

There must be a simpler way to express this. But I'm just too tired to figure it out.

The fact that I can't easily group by $_.version.major isn't helping.

Is there a bit of PowerShell fairy dust syntax I'm missing?

Code to load the JSON:

$org = "jessehouwing-brazil"
$pat = $env:AZURE_DEVOPS_PAT

$url = "https://dev.azure.com/$org"
$header = @{authorization = "Basic $([Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(".:$pat")))"}

$tasks = Invoke-RestMethod -Uri "$url/_apis/distributedtask/tasks?allversions=true" -Method Get -ContentType "application/json" -Headers $header | ConvertFrom-Json
$taskMetadata = $tasks.value

CodePudding user response:

Hmmm ... I actually already gave up when you wrote you have a solution. I've got errors trying to import your JSON data. It kind of worked this way:

$InputData = 
    Get-Content -Path D:\sample\data.json |
        ConvertFrom-Json -AsHashtable

$InputData | 
    Select-Object -Property Name, ID, 
        @{
            Name = 'Version'
            Expression = {[version]"$($_.version.major).$($_.version.minor).$($_.version.patch)"}
        },
        @{
            Name = 'Major'
            Expression = {"$($_.version.major)"}
        } |
    Group-Object -Property Name |
        ForEach-Object {
            $_.Group | 
                Group-Object -Property Major |
                ForEach-Object {
                    $_.Group | 
                    Sort-Object -Property Version | 
                        Select-Object -Last 1 -Property Name, ID, Version
                }
        }

CodePudding user response:

Here is one way to do it, inline comments should explain the thought process:

$uri = 'https://gist.githubusercontent.com/jessehouwing/1ceecd510f004c7b24ec68b280ab3f8f/raw/1aeab5a06d409dbff4b6791bafa0357e560284c0/data.json'
# group the objects by name first and enumerate
Invoke-RestMethod $uri | ConvertFrom-Json -AsHashtable | Group-Object name | ForEach-Object {
    # if name is empty, skip this object
    if(-not $_.Name) {
        return
    }

    # group the objects by major and enumerate them
    foreach($group in $_.Group | Group-Object { $_['version']['major'] }) {
        # sort groups by version
        $group.Group | Sort-Object { [version]::new.Invoke(@($_['version']['major', 'minor', 'patch'])) } |
            # pick the highest version in this major group
            Select-Object -Last 1
    }
} | Select-Object id, name, version
  • Related