mirror of
https://github.com/freedbygrace/Split-ObjectList.git
synced 2026-07-26 11:28:19 +00:00
332 lines
21 KiB
PowerShell
332 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..$(Get-Random -Minimum 100000 -Maximum 300000)) -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..$(Get-Random -Minimum 100000 -Maximum 300000)
|
|
$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.Percentage = $Percentage
|
|
$OutputObjectProperties.MemberCount = 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.MemberCount = $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
|