通过切片现有的 arraylist 创建 Arraylist 的 Arraylist

Creating Arraylist of Arraylist by slicing existing arraylist

我定义了以下变量

$A =  New-Object -TypeName "System.Collections.ArrayList"

现在我向其中添加 n 个元素:

$A.Add(1..n)

现在我想将 $A 分成 p 部分,每个部分有 k 个元素(如果 p*k>$A.count,最后一个可能有更少的元素)。 我该怎么做?

您可以使用一个函数将一个数组拆分成几个更小的数组。 在该函数的略微改编版本下方找到 here:

function Split-Array {
    [CmdletBinding(DefaultParametersetName = 'ByChunkSize')]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        $Array,

        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'ByChunkSize')]
        [ValidateRange(1,[int]::MaxValue)]
        [int]$ChunkSize,

        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'ByParts')]
        [ValidateRange(1,[int]::MaxValue)]
        [int]$Parts
    )

    $items = $Array.Count
    switch ($PsCmdlet.ParameterSetName) {
        'ByChunkSize'  { $Parts = [Math]::Ceiling($items / $ChunkSize) }
        'ByParts'      { $ChunkSize = [Math]::Ceiling($items / $Parts) }
        default        { throw "Split-Array: You must use either the Parts or the ChunkSize parameter" }
    }

    # when the given ChunkSize is larger or equal to the number of items in the array
    # use TWO unary commas to return the array as single sub array of the result.
    if ($ChunkSize -ge $items) { return ,,$Array }

    $result = for ($i = 1; $i -le $Parts; $i++) {
        $first = (($i - 1) * $ChunkSize)
        $last  = [Math]::Min(($i * $ChunkSize) - 1, $items - 1)
        ,$Array[$first..$last]
    }

    return ,$result
}

在您的情况下,您可以像这样使用它:

$p = 4  # the number of parts you want
$subArrays = Split-Array $A.ToArray() -Parts $p

$k = 4  # the max number items in each part
$subArrays = Split-Array $A.ToArray() -ChunkSize $k

这是我想出的一个函数,用于分块 System.Collections.ArrayList into a nested array list of p parts. It uses a System.Collections.Specialized.OrderedDictionary to group the size k chunks by index / chunksize, which is then rounded down to the nearest integer using System.Math.Floor。然后它只获取键从 0$Parts 的组。

function Split-ArrayList {
    [CmdletBinding()]
    param (
        # Arraylist to slice
        [Parameter(Mandatory=$true)]
        [System.Collections.ArrayList]
        $ArrayList,
        # Chunk size per part
        [Parameter(Mandatory=$true)]
        [ValidateRange(1, [int]::MaxValue)]
        [int]
        $ChunkSize,
        # Number of parts
        [Parameter(Mandatory=$true)]
        [ValidateRange(1, [int]::MaxValue)]
        [int]
        $Parts
    )

    # Group chunks into hashtable
    $chunkGroups = [ordered]@{}
    for ($i = 0; $i -lt $ArrayList.Count; $i++) {

        # Get the hashtable key by dividing the index by the chunk size
        # Round down to nearest integer using Math.Floor
        [int]$key = [Math]::Floor($i / $ChunkSize)

        # Add new arraylist for key if it doesn't exist
        # ContainsKey is not supported for ordered dictionary
        if ($chunkGroups.Keys -notcontains $key) {
            $chunkGroups.Add($key, [System.Collections.ArrayList]::new())
        }

        # Add number to hashtable
        [void]$chunkGroups[$key].Add($ArrayList[$i])
    }

    # Create nested ArrayList of parts
    $result = [System.Collections.ArrayList]::new()
    for ($key = 0; $key -lt $Parts; $key++) {
        [void]$result.Add($chunkGroups[$key])
    }

    $result
}

用法:

$A = [System.Collections.ArrayList]::new(1..10)

Split-ArrayList -ArrayList $A -ChunkSize 4 -Parts 1 | 
    ForEach-Object { "{ " + ($_ -join ", ") + " }" }

# { 1, 2, 3, 4 }

Split-ArrayList -ArrayList $A -ChunkSize 4 -Parts 2 | 
    ForEach-Object { "{ " + ($_ -join ", ") + " }" }

# { 1, 2, 3, 4 }
# { 5, 6, 7, 8 }

Split-ArrayList -ArrayList $A -ChunkSize 4 -Parts 3 | 
    ForEach-Object { "{ " + ($_ -join ", ") + " }" }

# { 1, 2, 3, 4 }
# { 5, 6, 7, 8 }
# { 9, 10 }

注意: 我并没有真正考虑到您可能想要排除的情况 Parts,所以我将每个参数都设置为强制性的。您可以修改函数,使其更灵活地处理不同的输入。