pacokent
pacokent

Reputation: 63

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!

Upvotes: 1

Views: 1079

Answers (3)

GaryG
GaryG

Reputation: 13

My use-case for this is populating a hash with some unknown number of values from an array where I need a fixed size array value for the hash entries based on other criteria in my hash's values. Lets say my array turns out to have 3422 elements and I need 100 per hash value plus whatever's left over for the last element added to the hash. Then: [Math]::Floor(3422/100) will give you the number of hash elements needed. I use 3 counters: $i, $j and $k

For instance:

$myArray = (get-<whatever> |select -exp Name)
$mySizeLimit = 100
$total = $myArray.count
$buckets = [math]::Floor($total/$mySizeLimit)

$myWrkHash = @{}

$j = 0; $k = 0;

for ($i = 0; $i -LE $buckets; $i++) {
    if($i -LT $buckets) {
        $k = $j + ($mySizeLimit - 1)
        $myWrkHash.Add($i, $myArray[$j..$k])
        $j+=$mySizeLimit
    } elseif($i -EQ $buckets) {
        $k = $total - 1
        $myWrkHash.Add($i, $myArray[$j..$k])
    }
}

Upvotes: 0

mklement0
mklement0

Reputation: 437208

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
  ]
]

Upvotes: 3

Abraham Zinala
Abraham Zinala

Reputation: 4694

If you keep track of the arrays created, you should be able to get the results you're after:

Function Split-Array {
    Param(
        [object]$InputObject,
        [int]$Chunks
    )
    $track = 1
    while ($InputObject.Count -gt 0 -and $track -le $Chunks) {
        $chunk_size = [Math]::Min($InputObject.Count, [Math]::Ceiling($InputObject.Count / ($Chunks - $track + 1)))
        $chunk = $InputObject[0..($chunk_size - 1)]
        $InputObject = $InputObject[$chunk_size..($InputObject.Count - 1)]
        ,$chunk
        $track++
    }
}

The while loop starts, and it will keep executing as long as either of these conditions are met:

  • $array.Count -gt 0: The count of elements in $array is greater than 0. This means that there are still elements in $array that need to be split into separate arrays.

  • $arrayIndex -le $arrayCount: The number of arrays created so far is less than or equal to $arrayCount. This means that you haven't created the desired number of arrays yet.

Upvotes: 1

Related Questions