3

I have a basic array in which I am using array_chunk to divide it into 3 elements.

$array = array(
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
);

$chunk = array_chunk($array, 3);

The result is as follows

[
   [
     "a",
     "b",
     "c"
   ],
   [
     "d",
     "e",
     "f"
   ],
   [
     "g",
     "h"
   ]
]

(Las chunk have 2 elements)

In case the last "chunk" has only 2 elements, how can I move down an element of the first chunk so that the first element has 2?

It should look like this:

[
   [
     "a",
     "b",
   ],
   [
     "c"
     "d",
     "e",
   ],
   [
     "f"
     "g",
     "h
   ]
]

(First chunk have 2 elements)

cmbuckley
  • 40,217
  • 9
  • 77
  • 91
Cristian Bustos
  • 357
  • 7
  • 20

4 Answers4

3

The easiest way are mutliple executions of erray_reverse: first reverse the entire array, then split, then reverse every chunk and then, lastly, the array of chunks.

As a one-liner, it loooks like this: $chunk = array_reverse(array_map('array_reverse', array_chunk(array_reverse($array), 3)));

Mind however that reversing an array is an expensive operation, so if you array is acutally alrger than in your example, this is not a recommended way.


More effective but also more code: Calculate the number of elements need to be in the first chunk using modulo: $first_chunk_size = count($array) % 3;

Next, to avoid an empty array if array size is a multiple of chunk size, correct the chunk size if modulo yields 0: $first_chunk_size = $first_chunk_size == 0 ? 3 : $first_chunk_size;

then, cut off the first part: $first_chunk = array_slice($array, 0, $first_chunk_size);

next, split the rest of the array: $chunks = array_chunk(array_slice($array,$first_chunk_size),3);

then combine the first chunk and the other chunks into one array: array_unshift($chunks, $first_chunk);

Full code of the second approach:

$array = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
];

$first_chunk_size = count($array) % 3;
$first_chunk_size = $first_chunk_size == 0 ? 3 : $first_chunk_size;

$first_chunk = array_slice($array, 0, $first_chunk_size);
$chunks = array_chunk(array_slice($array,$first_chunk_size),3);
array_unshift($chunks, $first_chunk);

var_dump($chunks);

(obviously this code can be simplified by not doing everything step-by-step)


EDIT: Having read your comments to the other replies, you can modify this approach a little and turn it into a recusrive function. With some code-cleanup,. it may look like this:

$array = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g'
];

function custom_array_chunk ($array) {
    $first_chunk_size = count($array) % 3;
    if ($first_chunk_size == 0) {
        $chunks = array_chunk($array, 3);
    } else {
        $chunks = custom_array_chunk(array_slice($array,2));
        array_unshift($chunks, array_slice($array, 0, 2));
    }
    return $chunks;
}

var_dump(custom_array_chunk($array));

This will work for both cases, 1 and 2 elements "left over" and will yield exactly what you told us.

Demo

Johannes H.
  • 5,875
  • 1
  • 20
  • 40
  • You don't need to calculate `$chunk_elements`, because `count($array) % 3` will return what you need. – cmbuckley Nov 23 '18 at 16:51
  • Waait, nevermind. I understood your comment. You are correct. – Johannes H. Nov 23 '18 at 16:53
  • Edited accordingly. – Johannes H. Nov 23 '18 at 16:55
  • Sorry, I do not speak any spanish. – Johannes H. Nov 23 '18 at 17:14
  • 1
    Either waym @CristianMeza, see my edit in the reply above. – Johannes H. Nov 23 '18 at 17:20
  • Edit: Thanks for your detailed response. The array can return 1, 2 or 3 elements. If I return 2, I move the last element of the first chunk down (question of the thread), but in case the last chunk returns only 1 element, as I can leave the first chunk with 1 element, get the first element of the second chunk and leave both the first and the second chunk with 2 elements? I know it costs a bit to understand it, but the result would be the following: http://sandbox.onlinephpfunctions.com/code/1abf11ce7243e5de7dc16e3809322943e0205f34 Cheerz – Cristian Bustos Nov 23 '18 at 17:50
  • It has worked perfectly for me. Very grateful for your help @JohannesH. Cheerz – Cristian Bustos Nov 23 '18 at 18:00
1

You could reverse you array first using array_reverse, then use array_chunk. Then reverse the inner arrays, and reverse the outer arrays again:

$array = array(
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
    'g',
    'h'
);
$array = array_reverse($array);
$chunk = array_chunk($array, 3);
$array = array_map('array_reverse', $chunk);
print_r(array_reverse($array));

Demo

Result

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

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

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

)
The fourth bird
  • 154,723
  • 16
  • 55
  • 70
  • Thanks for the help. I have another question, in case the last row of the chunk has only 1 element, how can I take only 1 element (last element) of the first row and move it down? Thanks – Cristian Bustos Nov 23 '18 at 16:34
  • @CristianMeza it sounds you want to combine the two chunks if there is a chunk with only a single element? That sounds like a separate question to me. – cmbuckley Nov 23 '18 at 16:48
  • @cmbuckley I think he actually wants to have a minimum number of 2 elements in the first chunk and the last chunk, preferring the last chunk to have 3 over the first. See my reply for a solution. – Johannes H. Nov 23 '18 at 16:51
  • @CristianMeza Not really sure what you mean. That sounds like a different question. Something like: https://3v4l.org/8U52a – The fourth bird Nov 23 '18 at 16:51
  • @CristianMeza Rather than us guessing, if you post a different question with expected output, that's the easiest way to get the best answer. – cmbuckley Nov 23 '18 at 16:52
  • The following can happen: The last chunk of the array can return 1, 2 or 3 elements. If I return 2, I move the last element of the first chunk down (question of the thread), but in case the last chunk returns only 1 element, as I can leave the first chunk with 1 element, get the first element of the second chunk and leave both the first and the second chunk with 2 elements? I know it costs a bit to understand it, but the result would be the following: http://sandbox.onlinephpfunctions.com/code/1abf11ce7243e5de7dc16e3809322943e0205f34 – Cristian Bustos Nov 23 '18 at 17:03
  • @CristianMeza See my change Edit in my reply. – Johannes H. Nov 23 '18 at 17:20
1

Functionally, the multiple array_reverse approach is quite understandable:

$chunk = array_reverse(array_map('array_reverse', array_chunk(array_reverse($array), 3)));

However, it's quite expensive, especially for larger arrays. Another way is to slice elements off the end of the array and prepend them to your chunked array:

$size = 3;
$chunk = array();
while (count($array) > 0) {
    array_unshift($chunk, array_slice($array, -$size));
    $array = array_slice($array, 0, -$size);
}

This works out about 20 times faster (over 100k iterations) for your array, and is about 1000 times faster for an array containing 600+ items.

cmbuckley
  • 40,217
  • 9
  • 77
  • 91
0

You can prepend empty datas to your array, in sort of array_chunk be compliant to your need.

Then remove these prepended datas.

$chunk = 3;
$nbToPrepend = ($chunk - count($array) % $chunk);
$prepend = array_fill(0, $nbToPrepend, null);
$result = array_chunk(array_merge($prepend, $array), $chunk);
$result[0] = array_slice($result[0], $nbToPrepend);
Lilian
  • 21
  • 6