Last active
September 8, 2022 05:26
-
-
Save Jaykul/2700d288b9f541e8d45cbbaf83768c63 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PSEquality : System.Collections.Generic.EqualityComparer[PSObject] { | |
[scriptblock]$Comparer = { $_ } | |
[bool] Equals([PSObject]$first, [PSObject]$other) { | |
return ($first | ForEach-Object $this.Comparer) -eq ($other | ForEach-Object $this.Comparer) | |
} | |
[int] GetHashCode([PSObject]$module) { | |
return $module.GetHashCode() | |
} | |
} | |
function Optimize-Dependency { | |
<# | |
.SYNOPSIS | |
Optimize a set of objects by their dependencies | |
.EXAMPLE | |
Find-Module TerminalBlocks, PowerLine | | |
Optimize-Dependency -Properties Name, Version -Dependency { | |
$_.Dependencies.Name.ForEach{ Find-Module $_ } | |
} | |
#> | |
[Alias("Sort-Dependency")] | |
[OutputType([Array])] | |
[CmdletBinding(DefaultParameterSetName = "ByPropertyFromInputObject")] | |
param( | |
# The path to a RequiredModules file | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "CustomEqualityFromPath")] | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "ByPropertyFromPath")] | |
[string]$Path, | |
# The objects you want to sort | |
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "CustomEqualityFromInputObject")] | |
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "ByPropertyFromInputObject")] | |
[PSObject[]]$InputObject, | |
# A list of properties used with Compare-Object in the default equality comparer | |
# Since this is in RequiredModules, it defaults to "Name", "Version" | |
[Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "ByPropertyFromInputObject")] | |
[Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "ByPropertyFromPath")] | |
[string[]]$Properties = @("Name", "Version"), | |
# A custom implementation of the equality comparer for the InputObjects | |
# Must accept two arguments, and return $true if they are equal, $false otherwise | |
# InputObjects will only be added to the output if this returns $false | |
# The default EqualityFilter compares based on the $Properties | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "CustomEqualityFromInputObject")] | |
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "CustomEqualityFromPath")] | |
[scriptblock]$EqualityFilter = { !($args[0] | Compare-Object $args[1] -Property $Properties) }, | |
# A ScriptBlock to calculate the dependencies of the InputObjects | |
# Defaults to a scriptblock that works for Find-Module and Get-Module | |
[Parameter(ValueFromPipelineByPropertyName)] | |
[ScriptBlock]$Dependency = { | |
if ($_ -is [psmoduleinfo]) { | |
$_.RequiredModules.Name.ForEach{ Get-Module $_ } | |
} else { | |
$_.Dependencies.Name.ForEach{ Find-Module $_ } | |
} | |
} | |
) | |
begin { | |
if ($Path) { | |
$null = $PSBoundParameters.Remove("Path") | |
ImportRequiredModulesFile $Path | FindModuleVersion | Optimize-Dependency @PSBoundParameters | |
return | |
} | |
if ($null -eq $Optimize_Dependency_Results) { | |
$Optimize_Dependency_Results = [System.Collections.Generic.HashSet[PSObject]]::new([PSEquality]::new($Properties, $EqualityFilter)) | |
} | |
} | |
process { | |
$null = $PSBoundParameters.Remove("InputObject") | |
Write-Debug "ENTER: Optimize-Dependency $(@($Optimize_Dependency_Results).Count): $(@($InputObject | Select-Object $Properties | ForEach-Object { $_.PsObject.Properties.Value}) -join ',')" | |
$InputObject | ForEach-Object $Dependency | Optimize-Dependency @PSBoundParameters | |
$InputObject | Sort-Object $Properties | ForEach-Object { | |
Write-Verbose "Adding $(@($_ | Select-Object $Properties | ForEach-Object { $_.PsObject.Properties.Value}) -join ',')" | |
if ($Optimize_Dependency_Results.Add($_)) { | |
$_ | |
} | |
} | |
} | |
} | |
# Trivial example, using already imported modules locally | |
Import-Module PowerShellGet -Max 2.99 -Passthru | Optimize-Dependency | |
# Easy example, searching the gallery | |
Find-Module TerminalBlocks, PowerLine | Optimize-Dependency | |
# The worst case that I know of: | |
Find-Module VMware.PowerCLI | Optimize-Dependency |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm going to add this to Jaykul/RequiredModules@main...feature/sorting