Files
Split-ObjectList/Split-ObjectList.ps1
T
2023-10-21 21:59:28 -04:00

330 lines
21 KiB
PowerShell

## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx
#region Function Split-ObjectList
Function Split-ObjectList
{
<#
.SYNOPSIS
Provides the functionality to split a list of objects into chunks based on the specified mode.
.DESCRIPTION
This is the more performant method of breaking an object into chunks for various use cases.
.PARAMETER ByPercentage
Places the function into "ByPercentage" mode. This will split the list of objects based on the provided percentage list.
.PARAMETER PercentageList
The list of percentages to split the list of objects by.
.PARAMETER InputObjectList
A valid list of objects that will be used for splitting.
.PARAMETER ContinueOnError
Ignore errors.
.EXAMPLE
$SplitObjectListResult = Split-ObjectList -ByPercentage -PercentageList @(5, 10, 15, 20, 25, 25) -InputObjectList (1..200000) -Verbose
Write-Output -InputObject ($SplitObjectListResult)
.EXAMPLE
$SplitObjectListParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$SplitObjectListParameters.ByPercentage = $True
$SplitObjectListParameters.PercentageList = New-Object -TypeName 'System.Collections.Generic.List[UInt32]'
$SplitObjectListParameters.PercentageList.Add(5)
$SplitObjectListParameters.PercentageList.Add(10)
$SplitObjectListParameters.PercentageList.Add(15)
$SplitObjectListParameters.PercentageList.Add(20)
$SplitObjectListParameters.PercentageList.Add(25)
$SplitObjectListParameters.PercentageList.Add(25)
$SplitObjectListParameters.InputObjectList = 1..200000
$SplitObjectListParameters.Verbose = $True
$SplitObjectListParameters.ContinueOnError = $False
$SplitObjectListResult = Split-ObjectList @SplitObjectListParameters
Write-Output -InputObject ($SplitObjectListResult)
.NOTES
Most of the time, various "List" type objects are used with index removal and so forth, which can work, but is not the most performant.
.LINK
https://stackoverflow.com/questions/56990719/split-array-by-percentage
.LINK
https://www.infoworld.com/article/3666499/how-to-work-with-a-priority-queue-in-net-6.html
#>
[CmdletBinding(ConfirmImpact = 'Low', DefaultParameterSetName = 'ByPercentage')]
[OutputType([System.Management.Automation.PSObject[]])]
Param
(
[Parameter(Mandatory=$True, ParameterSetName = 'ByPercentage')]
[ValidateNotNullOrEmpty()]
[Switch]$ByPercentage,
[Parameter(Mandatory=$True, ParameterSetName = 'ByPercentage')]
[ValidateNotNullOrEmpty()]
[System.UInt32[]]$PercentageList,
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[System.Object[]]$InputObjectList,
[Parameter(Mandatory=$False)]
[Switch]$ContinueOnError
)
Begin
{
Try
{
$DateTimeLogFormat = 'dddd, MMMM dd, yyyy @ hh:mm:ss.FFF tt' ###Monday, January 01, 2019 @ 10:15:34.000 AM###
[ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)}
$DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347###
[ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)}
$DateFileFormat = 'yyyyMMdd' ###20190403###
[ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)}
$DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354###
[ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)}
$TextInfo = (Get-Culture).TextInfo
$LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LoggingDetails.Add('LogMessage', $Null)
$LoggingDetails.Add('WarningMessage', $Null)
$LoggingDetails.Add('ErrorMessage', $Null)
$CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters)
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters)
[ScriptBlock]$ErrorHandlingDefinition = {
Param
(
[Int16]$Severity,
[Boolean]$ContinueOnError
)
$ExceptionPropertyDictionary = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ExceptionPropertyDictionary.Add('Message', $_.Exception.Message)
$ExceptionPropertyDictionary.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
#$ExceptionPropertyDictionary.Add('ExitCode', $Script:LASTEXITCODE)
#$ExceptionPropertyDictionary.Add('Script', $_.InvocationInfo.ScriptName)
$ExceptionPropertyDictionary.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ExceptionPropertyDictionary.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ExceptionPropertyDictionary.Add('Code', $_.InvocationInfo.Line.Trim())
$ExceptionMessageList = New-Object -TypeName 'System.Collections.Generic.List[String]'
ForEach ($ExceptionProperty In $ExceptionPropertyDictionary.GetEnumerator())
{
$ExceptionMessageList.Add("[$($ExceptionProperty.Key): $($ExceptionProperty.Value)]")
}
$LogMessageParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LogMessageParameters.Message = $ExceptionMessageList -Join ' '
$LogMessageParameters.Verbose = $True
Switch ($Severity)
{
{($_ -in @(1))} {Write-Verbose @LogMessageParameters}
{($_ -in @(2))} {Write-Warning @LogMessageParameters}
{($_ -in @(3))} {Write-Error @LogMessageParameters}
}
Switch ($ContinueOnError)
{
{($_ -eq $False)}
{
Throw
}
}
}
#Determine the date and time we executed the function
$FunctionStartTime = (Get-Date)
[String]$FunctionName = $MyInvocation.MyCommand
[System.IO.FileInfo]$InvokingScriptPath = $MyInvocation.PSCommandPath
[System.IO.DirectoryInfo]$InvokingScriptDirectory = $InvokingScriptPath.Directory.FullName
[System.IO.FileInfo]$FunctionPath = "$($InvokingScriptDirectory.FullName)\Functions\$($FunctionName).ps1"
[System.IO.DirectoryInfo]$FunctionDirectory = "$($FunctionPath.Directory.FullName)"
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is beginning. Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Define Default Action Preferences
$ErrorActionPreference = 'Stop'
[String[]]$AvailableScriptParameters = (Get-Command -Name ($FunctionName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Function Parameter(s) = $($AvailableScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
[String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {Try {"-$($_.Key):$($_.Value.GetType().Name)"} Catch {"-$($_.Key):Unknown"}}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
[String]$ParameterSetName = $PSCmdlet.ParameterSetName
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Parameter Set Name: $($ParameterSetName)"
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Create an object that will contain the functions output.
$OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[System.Management.Automation.PSObject]'
}
Catch
{
$ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent)
}
Finally
{
}
}
Process
{
Try
{
Switch ($ParameterSetName)
{
{($_ -iin @('ByPercentage'))}
{
[UInt32]$RequiredPercentageListTotal = 100
[UInt32]$PercentageListTotal = ($PercentageList | Measure-Object -Sum).Sum
Switch ($PercentageListTotal -ne $RequiredPercentageListTotal)
{
{($_ -eq $True)}
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The percentage list must equal $($RequiredPercentageListTotal)."
Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose
Return
}
}
$InputObjectQueue = New-Object -TypeName 'System.Collections.Generic.Queue[System.Object]'
$InputObjectList | ForEach-Object {($InputObjectQueue.Enqueue($_))}
$InputObjectListCount = ($InputObjectList | Measure-Object).Count
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Input Object List Count: $($InputObjectListCount)"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$PercentageListCounter = 1
$PercentageListCount = ($PercentageList | Measure-Object).Count
For ($PercentageListIndex = 0; $PercentageListIndex -lt $PercentageListCount; $PercentageListIndex++)
{
$Percentage = $PercentageList[$PercentageListIndex]
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process percentage list entry $($PercentageListCounter) of $($PercentageListCount). Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Percentage: $($Percentage)%"
Write-Verbose -Message ($LoggingDetails.LogMessage)
Switch ($InputObjectQueue.Count -gt 0)
{
{($_ -eq $True)}
{
$InputObjectPercentageFormula = {[System.Math]::Round((($InputObjectListCount * $Percentage) / 100), 0)}
$InputObjectPercentageCalculation = $InputObjectPercentageFormula.InvokeReturnAsIs()
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Percentage Calculation: $($InputObjectPercentageCalculation)"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.Wave = $PercentageListCounter
$OutputObjectProperties.Count = 0
$OutputObjectProperties.Members = New-object -TypeName 'System.Collections.Generic.List[System.Object]'
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process wave $($OutputObjectProperties.Wave). Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add $($InputObjectPercentageCalculation) member(s) to wave $($OutputObjectProperties.Wave). Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$InputObjectCounter = 1
For ($InputObjectCounter = 1; $InputObjectCounter -le $InputObjectPercentageCalculation; $InputObjectCounter++)
{
$InputObject = $InputObjectQueue.Dequeue()
$OutputObjectProperties.Members.Add($InputObject)
}
$OutputObjectProperties.Members = $OutputObjectProperties.Members.ToArray()
$OutputObjectProperties.Count = $OutputObjectProperties.Members.Count
$OutputObject = New-Object -TypeName 'System.Management.Automation.PSObject' -Property ($OutputObjectProperties)
$OutputObjectList.Add($OutputObject)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Successfully completed the processing of wave $($OutputObjectProperties.Wave)."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are now $($InputObjectQueue.Count) objects remaining in the queue."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$PercentageListCounter++
}
}
}
}
Catch
{
$ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent)
}
Finally
{
}
}
End
{
Try
{
#Determine the date and time the function completed execution
$FunctionEndTime = (Get-Date)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($FunctionName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Log the total script execution time
$FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is completed."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
Catch
{
$ErrorHandlingDefinition.Invoke(2, $ContinueOnError.IsPresent)
}
Finally
{
$OutputObjectList = $OutputObjectList.ToArray()
Write-Output -InputObject ($OutputObjectList)
}
}
}
#endregion