Split an Array into N Arrays - PHP
Asked Answered
S

11

26

I have an array of 18 values:

$array = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r');

I want to split this array into 12 different arrays so it should look like this:

array(
    0 => array('a', 'b'),
    1 => array('c', 'd'),
    2 => array('e', 'f'),
    3 => array('g', 'h'),
    4 => array('i', 'j'),
    5 => array('k', 'l'),
    6 => array('m'),
    7 => array('n'),
    8 => array('o'),
    9 => array('p'),
   10 => array('q'),
   11 => array('r')
)

My function doesn't seem to work

function array_split($array, $parts){
    return array_chunk($array, ceil(count($array) / $parts));
}

$result = array_split($array, 12);

because I get 9 different arrays instead of 12. It would return

array(
    0 => array('a', 'b'),
    1 => array('c', 'd'),
    2 => array('e', 'f'),
    3 => array('g', 'h'),
    4 => array('i', 'j'),
    5 => array('k', 'l'),
    6 => array('m', 'n'),
    7 => array('o', 'p'),
    8 => array('q', 'r')
)

How would I go about doing this? Thanks.

Suomi answered 22/3, 2013 at 20:55 Comment(5)
What are you trying to accomplish exactly?Sparkle
@ExplosionPills I want to split an array of 13 values into 12 different arrays. It is like using the array_chunk function where it splits an array based on how many values per each array.Suomi
You haven't explained your requirements very well. Or, at all, actually.Cornetist
What I mean is something like Management requested that I get a list of countries, but the first two countries in our DB should be considered the same and need to be grouped together.Sparkle
@ExplosionPills OK, I have 13 categories in the DB that I want to group them into 12 arrays. If there are more than 12 categories, which there are, then insert the remaining values starting from the first array.Suomi
E
38

This simple function would work for you:

Usage

$array = range("a", "r"); // same as your array
print_r(alternate_chunck($array,12));

Output

Array
(
    [0] => Array
        (
            [0] => a
            [1] => b
        )

    [1] => Array
        (
            [0] => c
            [1] => d
        )

    [2] => Array
        (
            [0] => e
            [1] => f
        )

    [3] => Array
        (
            [0] => g
            [1] => h
        )

    [4] => Array
        (
            [0] => i
            [1] => j
        )

    [5] => Array
        (
            [0] => k
            [1] => l
        )

    [6] => Array
        (
            [0] => m
        )

    [7] => Array
        (
            [0] => n
        )

    [8] => Array
        (
            [0] => o
        )

    [9] => Array
        (
            [0] => p
        )

    [10] => Array
        (
            [0] => q
        )

    [11] => Array
        (
            [0] => r
        )

)

Update The above might not be useful for most cases ... here is another type of chunk

$array = range("a", "r"); // same as your array
print_r(fill_chunck($array, 5));

Output

Array
(
    [0] => Array
        (
            [0] => a
            [1] => b
            [2] => c
            [3] => d
        )

    [1] => Array
        (
            [0] => e
            [1] => f
            [2] => g
            [3] => h
        )

    [2] => Array
        (
            [0] => i
            [1] => j
            [2] => k
            [3] => l
        )

    [3] => Array
        (
            [0] => m
            [1] => n
            [2] => o
            [3] => p
        )

    [4] => Array
        (
            [0] => q
            [1] => r
        )

)

This would make sure the group at no time is more that 5 elements where the other one has no limitation

Function Used

function alternate_chunck($array, $parts) {
    $t = 0;
    $result = array();
    $max = ceil(count($array) / $parts);
    foreach(array_chunk($array, $max) as $v) {
        if ($t < $parts) {
            $result[] = $v;
        } else {
            foreach($v as $d) {
                $result[] = array($d);
            }
        }
        $t += count($v);
    }
    return $result;
}


function fill_chunck($array, $parts) {
    $t = 0;
    $result = array_fill(0, $parts - 1, array());
    $max = ceil(count($array) / $parts);
    foreach($array as $v) {
        count($result[$t]) >= $max and $t ++;
        $result[$t][] = $v;
    }
    return $result;
}
Electrify answered 2/5, 2013 at 18:44 Comment(2)
You are welcome .. but am not sure if that function would help you in all possible case ... see update for other possibilities .. let me know if you have any issues ...Electrify
How do I loop through each array_chunk? ThanksRandi
A
7

Allow me to be the first to offer a math-based, loopless solution.

The magic in the math is determining which portion of elements belongs in the first set of chunks where all columns are filled in each row versus which elements belong in the second set (if the set should even exist) where all columns except the right-most column are filled.

Let me draw what I'm talking about. The > marks the division between the two chunked arrays.

$size = 9;        -------------    $size = 9;        -------------
$maxRows = 4;   1 | A , B , C |    $maxRows = 3;     | A , B , C |
$columns = 3;   > |-----------|    $columns = 3;   1 | D , E , F |
$fullRows = 1;    | D , E |        $fullRows = 3;    | G , H , I |
                2 | F , G |                        > -------------
                  | H , I |                        2      n/a
                  ---------


$size = 18;        ---------    $size = 17;       -------------------------------------
$maxRows = 12;     | A , B |    $maxRows = 2;   1 | A , B , C , D , E , F , G , H , I |
$columns = 2;      | C , D |    $columns = 9;   > -------------------------------------
$fullRows = 6;     | E , F |    $fullRows = 1;  2 | J , K , L , M , N , O , P , Q |
                 1 | G , H |                      ---------------------------------
                   | I , J |
                   | K , L |
                 > ---------
                   | M |
                   | N |
                   | O |
                 2 | P |
                   | Q |
                   | R |
                   -----

Code: (Demo)

function double_chunk($array, $maxRows) {
    $size = count($array);
    $columns = ceil($size / $maxRows);
    $lessOne = $columns - 1;
    $fullRows = $size - $lessOne * $maxRows;
    
    if ($fullRows == $maxRows) {
        return array_chunk($array, $fullRows);  // all columns have full rows, don't splice
    }
    return array_merge(
               array_chunk(
                   array_splice($array, 0, $columns * $fullRows),  // extract first set to chunk
                   $columns
               ),
               array_chunk($array, $lessOne)   // chunk the leftovers
           );
}
var_export(double_chunk(range('a', 'i'), 3));

If you don't mind the iterated array_splice() calls, this is more brief and perhaps easier to follow (...perhaps not):

Code: (Demo)

function custom_chunk($array, $maxRows) {
    $size = count($array);
    $columns = ceil($size / $maxRows);
    $lessOne = $columns - 1;
    $fullRows = $size - $lessOne * $maxRows;
    
    for ($i = 0; $i < $maxRows; ++$i) {
        $result[] = array_splice($array, 0, ($i < $fullRows ? $columns : $lessOne));
    }
    return $result;
}
var_export(custom_chunk(range('a', 'r'), 12));
As answered 3/11, 2018 at 10:14 Comment(1)
this seems to be most elaborated and accurate answer among all, just answered too late. But worked for me. +1Clack
I
3
<?php
function slotify($array, $length)
{
    // Create a placeholder array and calculate the number of items
    // needed per slot (think card dealing LtoR).
    for(
        $slots = array_fill(0, $length, 0), $count=count($array), $i=0;
        $i<$count;
        $slots[$i % $length]++, $i++
    );

    // Now just take slices of the original array
    // depending on the calculated slot number and place in our slots
    foreach($slots as $k => $n) {
        $slots[$k] = array_splice($array, 0, $n);
    }

    return $slots;
}

var_export(slotify(['a','b','c','d','e','f'], 4));

Output:

array (
  0 => 
  array (
    0 => 'a',
    1 => 'b',
  ),
  1 => 
  array (
    0 => 'c',
    1 => 'd',
  ),
  2 => 
  array (
    0 => 'e',
  ),
  3 => 
  array (
    0 => 'f',
  ),
)

The intermediate array $slots holding number of items required would look like this for the example above:

array (
  0 => 2,
  1 => 2,
  2 => 1,
  3 => 1,
)

If you study the different combinations for the intermediate array. You need to work out the boundary point where the chunk size changes. My solution gravitated towards the following, which is pretty much mickmackusa's final solution. But this requires understanding the problem.

function slotify($array, $length)
{
    $count      = count($array);
    $chunk_size = ceil($count/$length);
    $boundary   = $count % $length;

    if($boundary === 0)
        $boundary = $length;

    for($i=0; $i<$length; $i++) {
        if($i === $boundary)
            $chunk_size--;
        $result[$i] = array_splice($array, 0, $chunk_size);
    }

    return $result;
}

Further refinements can be made, slicing is probably more efficient than splicing, and if you can evenly distribute throughout the array, you could shortcut.

Income answered 17/9, 2020 at 12:1 Comment(0)
E
2

You can use array_chunk and array_merge for this problem:

<?php 

$array = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r');
$chunked_arr = array_chunk($array,12);
$j = 0;
for($i = 0; $i < count($chunked_arr[0]); $i++){
    if(!($i % 2 == 0)){
        $first_combined[$j][$i % 2] = $chunked_arr[0][$i];
        $j++;
    } else {
    $first_combined[$j][$i % 2] = $chunked_arr[0][$i];
    }
}

$merged_array = array_merge($first_combined, $chunked_arr[1]); 

echo '<pre>';
print_r($merged_array);
 ?>

And You will get the result like this:

Array
(
    [0] => Array
        (
            [0] => a
            [1] => b
        )

    [1] => Array
        (
            [0] => c
            [1] => d
        )

    [2] => Array
        (
            [0] => e
            [1] => f
        )

    [3] => Array
        (
            [0] => g
            [1] => h
        )

    [4] => Array
        (
            [0] => i
            [1] => j
        )

    [5] => Array
        (
            [0] => k
            [1] => l
        )

    [6] => m
    [7] => n
    [8] => o
    [9] => p
    [10] => q
    [11] => r
)

This is what exactly you want.

Live Demo Here>>

Emancipator answered 3/5, 2013 at 10:10 Comment(1)
count($chunked_arr[0]) should be cached as a variable instead of called after each iteration of the loop. The same advice regarding $i % 2 -- you should not ask php to perform the same calculation on the same data.As
J
1

ceil(count($array) / $parts) would give 2, so each array is being filled up with 2 items until you dont have 2 items left. hence the last one has 1 item. this will work when you have a huge amount of data in the array, but not so much when you have a small amount of data.

Jenellejenesia answered 22/3, 2013 at 21:8 Comment(0)
S
1

What you are describing is not what array_chunk is made for. You should use array_slice() and calculate yourself which parts of the array you want to end up as new arrays. (and use a for loop to iterate over your original array)

Update:

Some calculations that could help you:

minimum_fill = floor(array_length / nr_buckets)
bigger_buckets_amount = array_length - (minimum_fill / nr_buckets)

Algorithm to fill buckets: Loop over the array, fill the first bigger_buckets_amount amount of buckets with (minimum_fill + 1), fill the rest of the buckets with minimum_fill

Start answered 22/3, 2013 at 21:8 Comment(1)
Are you able you crystallize these hints into a complete working snippet?As
G
1

Compile that and see if it does for you:

<?php

$array = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm');

$sliceA = 0;
$sliceB = 2;

$final = array(array_slice($array, $sliceA, $sliceB));


for ($i=$sliceB; $i<sizeof($array); $i++)
{
    $final[$sliceB-1] = array($array[$i]);
    $sliceB++;
}

var_dump($final);
Georgy answered 22/3, 2013 at 22:14 Comment(1)
The OP wishes to only supply the input array and a max number of subarrays (which may have unequal counts). You answer is requiring the input array plus $sliceA and sliceB. Please re-read the question.As
M
1
<?php
$array = range('a','r');
$length = array(2=>6,1=>6); // 2=>6 means -- first six elements of new array will have 2 elements each and then, 1=>6 means -- next six elements of new array will have 1 element each
$target = array(); // or use []  in PHP 5.4
foreach($length as $i=>$times) {
    while($times>0){
        $target[] = array_splice($array, 0, $i);
        $times--;
    }
}
print_r($target);
?>
Mona answered 5/5, 2013 at 11:30 Comment(1)
This is not very versatile -- it expects the developer to evaluate and hardcode the preferred chunking algorithm. Rather unhelpful if you ask me. If the developer is going to bother doing this, why don't they just manually define all of the new data structure while they are at it?As
C
0

This will do it for you!
Here, I used my function smallify() to break an array of 15 elements into 3 arrays of 5 elements each.

<?php

$bigArray = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);

echo ("<pre>");
print_r (smallify($bigArray, 3));
echo ("<pre/>");


function smallify($arr, $numberOfSlices){

  $sliceLength = sizeof($arr) /$numberOfSlices;
  for($i=1; $i<=$numberOfSlices; $i++){

       $arr1 = array_chunk($arr, $sliceLength*$i);
       return $arr1;
       unset($arr1);

   }

}
?>

Result

Array
(
[0] => Array
    (
        [0] => 1
        [1] => 2
        [2] => 3
        [3] => 4
        [4] => 5
    )

[1] => Array
    (
        [0] => 6
        [1] => 7
        [2] => 8
        [3] => 9
        [4] => 10
    )

[2] => Array
    (
        [0] => 11
        [1] => 12
        [2] => 13
        [3] => 14
        [4] => 15
    )

)
Craftwork answered 29/9, 2015 at 8:55 Comment(1)
What good is unset() if it is written after a return? Why bother with a loop if you are going to kill it on the first iteration? Please provide a working demo link, because this answer doesn't smell right.As
I
-1

I believe the problem is that you are using a size of 2 when using array_chunk. This ensures that each new array created has two items in it if possible. This causes the function to run out of variables to put into the new arrays by the time you get to 10. You can find the manual on the function here http://php.net/manual/en/function.array-chunk.php Hope this helps

Imitation answered 5/5, 2013 at 19:33 Comment(1)
This is an observation of the issue, but does not attempt to provide a working resolution. Not An Answer.As
G
-1

Can you try using the following simple function?

$cols = array2cols($array,12);

function array2cols($array,$n){
    $groups = array();
    for($i=0;$i<$n;$i++){
        $groups[$i] = array();
    }
    $col = 0;
    foreach($array as $row){
        $groups[$col][] = $row;
        $col = ($col+1)%$n;
    }
    return $groups;
}
Gildea answered 1/12, 2019 at 9:43 Comment(1)
This is not the desired result. Proof: 3v4l.org/Vn3UM the first subarray should contain values a and b, but your code show a and m. If re-reading the question doesn't help, see the graphic representations in my answer.As

© 2022 - 2024 — McMap. All rights reserved.