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