Search code examples
arrayspowershellpowershell-3.0

Powershell split an array into 5 arrays with equal length


Hello everyone hope you are all doing great! been searching but cannot get it right :( could it be possible for you to help me, please? Need to split an array into 5 arrays with equal length, for example.

$MainArray = @(1,2,3,4,5,6,7,8,9,10,11)

Result:

array1 = 1,2,3

array2 = 4,5

array3 = 6,7

array4 = 8,9

array5 = 10,11

Each array as even as possible (order doesn't matters) has this and it splits but not as even as I would like to.

Currently, I have this (searched on the internet already)

function Split-Array {
[CmdletBinding()]
param(
    [Object] $inArray,
    [int]$parts
)
if ($inArray.Count -eq 1) { return $inArray }
$PartSize = [Math]::Ceiling($inArray.count / $parts)
$outArray = New-Object 'System.Collections.Generic.List[psobject]'
for ($i = 1; $i -le $parts; $i++) {
    $start = (($i - 1) * $PartSize)
    $end = (($i) * $PartSize) - 1
    if ($end -ge $inArray.count) {$end = $inArray.count - 1}
    $outArray.Add(@($inArray[$start..$end]))
}
return , $outArray
}
Split-array -inArray $MainArray -parts 5

This function splits the $MainArray into 5 arrays but not as even, the result is:

array1 = 1,2,3

array2 = 4,56

array3 = 7,8,9

array4 = 10,11

array5 = 11

It even errors adding 11 into 2 arrays. My brain is burned at this moment, haha any help would be much appreciated. thanks!


Solution

  • To perform the element distribution as requested - with extra elements getting added to the initial output arrays - use the following.

    function Split-Array {
      [CmdletBinding()]
      param(
        [object[]] $inArray,
        [int] $parts
      )
      [int] $partSize = [Math]::Floor($inArray.count / $parts)
      if ($partSize -eq 0) { throw "$parts sub-arrays requested, but the input array has only $($inArray.Count) elements." }
      $extraSize = $inArray.Count - $partSize * $parts
      $offset = 0
      foreach ($i in 1..$parts) {
        , $inArray[$offset..($offset + $partSize + [bool] $extraSize - 1)]
        $offset += $partSize + [bool] $extraSize
        if ($extraSize) { --$extraSize }
      }
    }
    

    Note:

    • [bool] casts are used as a convenient shortcut to map nonzero values to 1 and zero to 0, via using the resulting [bool] in the context of calculations.

    • .. - the range operator - is used to extract array slices from the input array, and also as a simple way to loop $parts times via a foreach loop.

    • , - the array constructor operator - is used in its unary form to output each array slice as a whole - see this answer for an explanation.

    Sample call, which uses ConvertTo-Json to visualize the results:

    Split-array -inArray (1..11) -parts 5 |
      ConvertTo-Json
    

    Output (5 arrays with 2-3 elements each):

    [
      [
        1,
        2,
        3
      ],
      [
        4,
        5
      ],
      [
        6,
        7
      ],
      [
        8,
        9
      ],
      [
        10,
        11
      ]
    ]