Internal/_SortByDependency.ps1
#Thank the FSM for the internets! # http://rosettacode.org/wiki/Topological_sort#PowerShell # http://stackoverflow.com/questions/8982782/does-anyone-have-a-dependency-graph-and-topological-sorting-code-snippet-for-pow function _SortByDependency { [cmdletbinding()] param( [Parameter(Mandatory, ValueFromPipeline, Position = 0)] [psobject[]]$Objects ) begin { Write-Verbose 'Sorting POSHOrigin objects by dependencies...' $set = @() } process { foreach ($item in $objects) { $set += $item } } end { Write-Debug "Objects to sort: $($set.Count)" $edgeList = @{} $set | ForEach-Object { $edgeList.Add($_.FullName, $_) } # Make sure we can use HashSet Add-Type -AssemblyName System.Core # Clone it so as to not alter original $currentEdgeList = [hashtable] (_CopyObject -DeepCopyObject $edgeList) $topologicallySortedElements = New-Object System.Collections.ArrayList $setOfAllNodesWithNoIncomingEdges = New-Object System.Collections.Queue $fasterEdgeList = @{} # Keep track of all nodes in case they put it in as an edge destination but not source $allNodes = New-Object -TypeName System.Collections.Generic.HashSet[object] -ArgumentList (,[object[]] $currentEdgeList.Keys) foreach($currentNode in $currentEdgeList.Keys) { $currentDestinationNodes = [array]$currentEdgeList[$currentNode].DependsOn if($currentDestinationNodes.Length -eq 0) { $setOfAllNodesWithNoIncomingEdges.Enqueue($currentNode) } foreach($currentDestinationNode in $currentDestinationNodes) { if(!$allNodes.Contains($currentDestinationNode)) { [void] $allNodes.Add($currentDestinationNode) } } # Take this time to convert them to a HashSet for faster operation if ($currentDestinationNodes) { $currentDestinationNodes = New-Object -TypeName System.Collections.Generic.HashSet[object] -ArgumentList (,[object[]] $currentDestinationNodes ) [void] $fasterEdgeList.Add($currentNode, $currentDestinationNodes) } } #Now let's reconcile by adding empty dependencies for source nodes they didn't tell us about foreach($currentNode in $allNodes) { if(!$currentEdgeList.ContainsKey($currentNode)) { [void] $currentEdgeList.Add($currentNode, (New-Object -TypeName System.Collections.Generic.HashSet[object])) $setOfAllNodesWithNoIncomingEdges.Enqueue($currentNode) } } $currentEdgeList = $fasterEdgeList while($setOfAllNodesWithNoIncomingEdges.Count -gt 0) { $currentNode = $setOfAllNodesWithNoIncomingEdges.Dequeue() [void] $currentEdgeList.Remove($currentNode) [void] $topologicallySortedElements.Add($currentNode) foreach($currentEdgeSourceNode in $currentEdgeList.Keys) { $currentNodeDestinations = $currentEdgeList[$currentEdgeSourceNode] if($currentNodeDestinations.Contains($currentNode)) { [void] $currentNodeDestinations.Remove($currentNode) if($currentNodeDestinations.Count -eq 0) { [void] $setOfAllNodesWithNoIncomingEdges.Enqueue($currentEdgeSourceNode) } } } } if($currentEdgeList.Count -gt 0) { throw $msgs.sbd_cyclic_error } $result = @() foreach ($item in $topologicallySortedElements) { if ($null -ne $edgeList[$item]) { $result += $edgeList[$item] } } Write-Debug "Objects sorted: $($result.Count)" return $result } } |