1

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!

1
  • First example is what I would want to achieve, get sub arrays as even as possible. Commented Feb 6, 2023 at 2:36

3 Answers 3

3

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
  ]
]
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.