14

I have an array which can have several items in it, e.g:

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
etc

I need the quickest way to restructure this array so that it has at most X items. So if I say that X is 3, the resulting array must be:

Item 1 , Item 2
Item 3, Item 4
Item 5, Item 6
etc

or if it has 7 items, it would be:

Item 1 , Item 2, Item 3,
Item 4, Item 5, 
Item 6, Item 7

What's the easiest way to do this?

I started with this, but it seems like there really must be an easier way:

foreach ($addressParts as $part)
{
    if (empty($part)) continue;
    if (empty($addressLines[$count]))  $addressLines[$count] = '';
    $addressLines[$count] .= $part;
    $count++;
    if ($count > 2) $count = 0;
}

Also, this won't work, because you will end up with this:

item 1, item 4, item 7
item 2, item 5
item 3, item 6

... which is wrong. Any thoughts?

UPDATE

If I start with:

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

I want to end with:

Array
(
    [0] => item 1, item 2, item 3
    [1] => item 4, item 5
    [2] => item 6, item 7
)

Make sense?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
rockstardev
  • 13,479
  • 39
  • 164
  • 296
  • array_chunk() perhaps? You just need to work out the chunk size – Mark Baker Oct 30 '13 at 14:08
  • 1
    Quickest as in fastest executing, or least lines/complexity? – Darsstar Nov 28 '13 at 09:40
  • Least lines, easiest to read. – rockstardev Nov 28 '13 at 09:44
  • I'm sorry, this does not make any sense to me. I don't see the difference between the examples. _X_ is a `variable unsigned integer` the array passed is of unknown length, and it has to be broken up sequentially following **what rules** exactly? if `X=3` break into three subarrays containing an equal amount of elements, if `X=7` break into... I don't get it. Either 3 and 7 are the only options available, and those 2 examples are the only result posible, or my brain is going to burn in derp – hanzo2001 Dec 02 '13 at 11:15
  • @hanzo2001 the two examples (wrong and good) differ in direction, the wrong one fills down first, the good one fills to the right, then down. The interesting part here is to know the length of each line befor starting to fill wich is kind of related to word wrapping for given line numbers instead of given line length. – Stephan B Dec 02 '13 at 11:51
  • I seem to be getting it... so if I have an array of length _N_, it should be broken into (how many sub arrays?) that have almost the same amount (preferably the same amount). So in your example I see 7 elements, these have to be broken into 3 subarrays but they shouldn't differ in as much as 1 element in length. Is this correct? – hanzo2001 Dec 02 '13 at 12:37
  • The output should be an array of 3 strings that each contain some items separated by ", " (as shown on your last output sample), or a two-dimensional array of individual items? – Robert Mikes Dec 04 '13 at 11:42
  • @coderama I can't believe I found 13 incorrect answers on this page! This page needs a spring clean. – mickmackusa Nov 03 '18 at 14:23

19 Answers19

5

This function combines elements to a new array as per your example. It handles any number of input elements.

function ReduceArray($input, $length) {
        $frac = $length / count($input);
        $frac = $frac + 0.0001;    // Offset for float calculations
        $index = 0.0;

        // Copy the elements, rolling over on $index
        $temp = array();
        foreach ($input as $part) {
                $i= floor($index);
                if (!isset($temp[$i])) {
                        $temp[$i] = array($part);
                } else {
                        $temp[$i][] = $part;
                }
                $index += $frac;
        }

        // Combine the sub arrays
        $output = array();
        foreach ($temp as $line) {
            $output[] = implode(', ', $line);
        }
        return $output;
}

$input = array('Item 1',  'Item 2',  'Item 3',  'Item 4',  'Item 5',  'Item 6', 'Item 7');
$output = ReduceArray($input, 3);
print_r($output);

Output

Array
(
    [0] => Item 1, Item 2, Item 3
    [1] => Item 4, Item 5
    [2] => Item 6, Item 7
)

Edit "fixed" the output as per the given output.

Edit See Comment for nine elements, tested for up to 12 elements. Thanks sectus

Stephan B
  • 3,671
  • 20
  • 33
  • cant we use only else part? the output remains same when we use only else part. – Rakhi Nov 29 '13 at 13:11
  • @vishal Yes, you are then initialising the array on the fly. I like to avoid this in my code, even when this disqualifies me for 'least lines' ; ) – Stephan B Dec 02 '13 at 11:34
  • 1
    Wrong result for 9 items – sectus Dec 03 '13 at 03:15
  • D'uh thanks for commenting,, don't know how I missed that. They failed me again, my trusty floats. – Stephan B Dec 04 '13 at 14:44
  • 1
    Unfortunately it depends on the offset, because the additions will eventually overflow. The `$frac += 0.0001;` version will fail for 1000 items. I guess we could compute the offset dynamically, something like `$frac += 1 / (count($input) *count($input));`. But even that will fail for 100000 items. Floats are funny that way. – Robert Mikes Dec 04 '13 at 15:42
  • Yes, that offset is tied to the expected number of elements, therefore I mentioned my test range. There are a couple of non-float answers here, they may be better for 1000s of elements but your dynamic offset looks good too. – Stephan B Dec 04 '13 at 15:58
  • Just made one that uses array_slice and dynamic offset + lengths, but ended up pretty much like the one from @ThomasWeinert. – Stephan B Dec 04 '13 at 16:27
  • It's wierd solution to use floats with this task – sectus Dec 05 '13 at 07:40
  • I'm not sure I follow why floats are useful in this answer, but this answer does provide the desired output. – mickmackusa Nov 03 '18 at 14:19
4

For each group, calculate to offset of the first element and the group length, copy that slice from the input array.

function array_group_elements($array, $groups) {
  $result = array();
  $count = count($array);
  // minimum in each group
  $limit = floor($count / $groups);
  // balance, modulo
  $overhead = $count % $groups;
  // for each group
  for ($i = 0; $i < $groups; ++$i) {
    // group offset, add 1 for each group that got a balance element
    $offset = ($i * $limit) + ($i < $overhead ? $i : $overhead);
    // length, add 1 if it is a group with balance element
    $length = $limit + ($i < $overhead ? 1 : 0);
    // copy slice from original array
    $result[] = array_slice($array, $offset, $length);
  }
  return $result;
}

$input = array('Item 1',  'Item 2',  'Item 3',  'Item 4',  'Item 5',  'Item 6', 'Item 7');
$grouped = array_group_elements($input, 3);
var_dump(
  array_map(
    function($group) {
      return implode(', ', $group);
    },
    $grouped
  )
);

Output:

array(3) {
  [0]=>
  string(22) "Item 1, Item 2, Item 3"
  [1]=>
  string(14) "Item 4, Item 5"
  [2]=>
  string(14) "Item 6, Item 7"
}

The function array_group_elements() loops over the $groups (3 times), not the $array (7 times).

ThW
  • 19,120
  • 3
  • 22
  • 44
3

I have two functions with different approaches.

function restructureArray1($array, $x)
    {
    $size = sizeof($array);
    if ($size < $x)
        return array_chunk($array, 1) + array_fill(0, $x, array());
        // chunk by 1 element and add missing empty elements

    $big_row_length = (int) ($size / $x) + 1;
    $big_rows_chunk = array_splice($array, 0, $size % $x * $big_row_length);
    // $big_rows_chunk contains items with big rows
    // $array now contains items with small rows
    return array_merge(array_chunk($big_rows_chunk, $big_row_length), array_chunk($array, $big_row_length - 1));
    // chunk and merge
    }

function restructureArray2($array, $x)
    {
    $size = sizeof($array);
    $small_row_length = (int) ($size / $x);
    $big_row_count = $size % $x;
    for ($i = 0; $i < $x; ++$i)
        {
        $length = $small_row_length + (int) ($i < $big_row_count); // type juggling
        $return [] = array_splice($array, 0, $length);
        // $return[] contains one row
        // $array now contains rest of array
        }
    return $return;
    }

To get result with array of strings you need just array_map it.

$x = 3;
$size = 7;
$array = range(1, $size);
$result1 = restructureArray1($array, $x);
$result2 = restructureArray2($array, $x);

var_dump(array_map(function($array)
            { return implode(', ', $array); }, $result2));

Demo

Relative links to documentation: array_splice, array_chunk, array_merge

P.S. The shortest solution


function restructureArray3($array, $x)
    {
    while($x)
        $return [] = array_splice($array, 0, ceil(sizeof($array) / $x--));
    return $return;
    }

Demo

sectus
  • 15,605
  • 5
  • 55
  • 97
  • +2. This works perfect. But adds empty items when length is less than `$x` –  Dec 05 '13 at 10:45
  • @GeorgeGarchagudashvili, all function could be changed easy for this behaviour. – sectus Dec 06 '13 at 02:56
  • sure, no prob. Just noted :)) –  Dec 06 '13 at 05:15
  • 1
    I think the call to implode() should be part of the function, like so: `$return [] = implode(', ', array_splice($array, 0, ceil(sizeof($array) / $x--)));`. Then it really is the shortest solution. Genious! You should remove the first two solutions, coz it just clutters your answer and hides the best solution! – Robert Mikes Dec 06 '13 at 10:28
3

I've come up with the following algorithm that keeps fitting the right number of items in each row; in each iteration, we round up the division of the number of remaining lines vs remaining items:

$a = range(1,7);

$len = count($a);
$res = array();
$max = 3;

$i = 0;
while ($i < $len) {
        // divide the remaining number of items by the maximum rows allowed
        // this gives the width for the current row
        $width = ceil(($len - $i) / $max);

        $res[] = join(', ', array_slice($a, $i, $width));

        // adjust pointer and reduce the maximum
        $i += $width;
        --$max;
}

print_r($res);

Demo

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
2
<?php
$source_array = array(
  'item 1',
  'item 2',
  'item 3',
  'item 4',
  'item 5',
  'item 6',
  'item 7',
  'item 8',
  'item 9',
  'item 10',
);


$k = 4;

// allocating cells
$allocated_cells_sizes = array();
$ik = 0;
foreach ($source_array as $value){
  if (! isset($allocated_cells_sizes[$ik])) $allocated_cells_sizes[$ik] = 0;
  $allocated_cells_sizes[$ik] ++;
  if (++$ik >= $k) $ik = 0;
}

// filling result array
$result = array();
foreach ($allocated_cells_sizes as $cells_sizes){
  $result[] = implode(', ', array_slice($source_array, 0, $cells_sizes));
  $source_array = array_slice($source_array, $cells_sizes, null);
}

print_r($result);

/**
 * Output
 * Array
    (
    [0] => item 1, item 2, item 3
    [1] => item 4, item 5, item 6
    [2] => item 7, item 8
    [3] => item 9, item 10
    )

 */
MaratMS
  • 430
  • 4
  • 9
2

For each group you need to calculate the maximum number of items that will fit in each segment, so I used the ceil(); function to always round up.

Input:

Array
(
    [0] => item1
    [1] => item2
    [2] => item3
    [3] => item4
    [4] => item5
    [5] => item6
    [6] => item7
)

Function:

function segment_array($array, $segment = '3'){

    // Count the items
    $count = count($array);

    // Create an array item for each segment
    for($i=0;$i<$segment;$i++){
        $offset += ($count - ($count - $chunk));
        $chunk = ceil(($count - $offset)/($segment - $i));
        $set[$i] = array_slice($array, $offset, $chunk);
        $new_array[] = implode(', ', $set[$i]);
    }

    return($new_array);
}

$segmented = segment_array($array, '3');

Output:

Array
(
    [0] => item1, item2, item3
    [1] => item4, item5
    [2] => item6, item7
)
Patrick Lyver
  • 389
  • 2
  • 7
2

The code:

$input = array('Item 1',  'Item 2',  'Item 3',  'Item 4',  'Item 5',  'Item 6',  'Item 7');
$height = 3;
$commonWidth = floor(count($input) / $height);
$remaining = count($input) % $height;
$i = 0;
for ($j = 0; $j < $height; $j++) {
    $width = $commonWidth + (0 < $remaining--);
    $output[] = implode(', ', array_slice($input, $i, $width));
    $i += $width;
}
print_r($output);

The output:

Array
(
    [0] => Item 1, Item 2, Item 3
    [1] => Item 4, Item 5
    [2] => Item 6, Item 7
)

I wonder if the problem description is incorrect and the required result is in fact a multi-dimensional array, not an array of concatenated strings. In that case just remove the call to implode:

$input = array('Item 1',  'Item 2',  'Item 3',  'Item 4',  'Item 5',  'Item 6',  'Item 7');
$height = 3;
$commonWidth = floor(count($input) / $height);
$remaining = count($input) % $height;
$i = 0;
for ($j = 0; $j < $height; $j++) {
    $width = $commonWidth + (0 < $remaining--);
    $output[] = array_slice($input, $i, $width);
    $i += $width;
}
print_r($output);

And the output is:

Array
(
    [0] => Array
        (
            [0] => Item 1
            [1] => Item 2
            [2] => Item 3
        )

    [1] => Array
        (
            [0] => Item 4
            [1] => Item 5
        )

    [2] => Array
        (
            [0] => Item 6
            [1] => Item 7
        )
)
Robert Mikes
  • 1,179
  • 8
  • 19
1

working for any number of items. and change in any number of columns, just change $x value.

<?php
    $arr = array('Item 1','Item 2','Item 3','Item 4','Item 5','Item 6', 'Item 7');
    $x = 3;
    $newArr = array();
    $j = 0;
    foreach($arr as $key=>$val){
        if($j == $x){
            $j = 0;
        }
        $newArr[$j][] = $val;
        $j++;
    }
    foreach($newArr as $key=>$val){
        $tempVal = implode(',', $val);
        $newArr[$key] = $tempVal;
    }
    print_r($newArr);
    ?>
harry
  • 1,007
  • 2
  • 10
  • 19
1

Another algorithm, because as I understand the question is to restructure the array in place, so the best efficiency can be achieved when no temporary arrays are created.

<?php
function group_array(&$array, $parts = 3)
{
    $size = count($array);
    $length = floor($size / $parts);
    $remains = $size % $parts;
    $done = 0;
    for($i = 0; $i < $parts; ++$i)
    {
        $real_length = $length + ($i < $remains ? 1 : 0);
        $array[$i] = $array[$done];
        for($j = $done + 1; $j < min($done + $real_length, $size); ++$j)
            $array[$i] .= ', ' . $array[$j];
        $done += $real_length;
    }
    for($i = $size; $i >= $parts ; --$i)
        unset($array[$i]);
}

?>

Test case #1:

<?php

$array = array("item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7");
group_array($array);

echo '<pre>' . print_r($array, true) . '</pre>';

?>

Output:

Array
(
    [0] => item 1, item 2, item 3
    [1] => item 4, item 5
    [2] => item 6, item 7
)

Test case #2:

<?php

$array = array("item 1", "item 2", "item 3", "item 4", "item 5", "item 6",
               "item 7", "item 8", "item 9", "item 10");
group_array($array);

echo '<pre>' . print_r($array, true) . '</pre>';

?>

Output:

Array
(
    [0] => item 1, item 2, item 3, item 4
    [1] => item 5, item 6, item 7
    [2] => item 8, item 9, item 10
)
mcserep
  • 3,231
  • 21
  • 36
1

If you're satisfied with a two-dimensional array, and you're happy if the last row can be much smaller than all the rest, here's a one-liner:

function split_array1($orig_array, $parts = 1) {
    return array_chunk($orig_array,ceil(count($orig_array)/$parts));    
}

Same thing, merging each piece into strings:

function split_array2($orig_array, $parts = 1) {
    $split = array_chunk($orig_array,ceil(count($orig_array)/$parts));
    foreach ($split as &$row) $row = join($row, ', ');
    return $split;
}

Output of three tests, with 7, 8, and 9 items:

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

Test:

$input = array('Item 1',  'Item 2',  'Item 3',  'Item 4',  'Item 5',  'Item 6', 'Item 7', 'Item 8',  'Item 9');
for ($size=7; $size<=count($input); $size++){
    $output = split_array2(array_slice($input, 0, $size), 3);
    print_r($output);
    echo '<br>';
}

This will match your examples exactly:

function split_array3($orig_array, $parts = 1) {
    $count = count($orig_array);
    for ($i=0, $index=0; $i<$parts; $i++) {
        $size_of_sub = ceil(($count - $index) / ($parts-$i));
        $split[$i] = join(array_slice($orig_array, $index, $size_of_sub), ', ');
        $index += $size_of_sub;
    }
    return $split;
}

Result:

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

Just for fun, here's a solution that uses recursion:

function recursive_split_array($orig_array, $num_sub_arrays = 1) {
    $size_of_sub = ceil(count($orig_array) / $num_sub_arrays);
    $split[0] = join(array_slice($orig_array, 0, $size_of_sub),', ');
    $split = array_merge( $split,
                          split_array(array_slice($orig_array, $size_of_sub,
                                                  count($orig_array)-$size_of_sub),$num_sub_arrays - 1));
    return $split;
}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
  • `split_array1()` does not provide the OP's desired output structure. Proof: https://3v4l.org/o2CBv `split_array2()` does not generate the correct result either. Proof: https://3v4l.org/HpnDJ After I changed `split_array()` to `recursive_split_array()` your "just for fun" snippet breaks more times than I can count. Proof: https://3v4l.org/FRGuB **`array_split3()` DOES work! Please either remove or correct all of the other functions so that your answer is 100% valuable to others.** – mickmackusa Nov 03 '18 at 13:42
  • (I am taking it upon myself to test all of the answers on this page because there is an astounding number of incorrect answers here.) – mickmackusa Nov 03 '18 at 13:42
0

Sorry it should be a comment but I can't do comments maybe because of my reputation...

When you have an array with 7 elements, do you know that you'll divide the array by 3 or you have to find the divisor ?

EDIT 1 Something like this ? :

$formatedAddress = array();
$divisor = 3;
$key = 0;
$counter =1;
foreach ($addressParts as $part)
{
    if(!empty($part){
        $formattedAddress[$key] = $part;
        if($counter != $divisor){
           $counter++;
        }else{
            $counter = 1;
            $key++;
        }
    }

}

EDIT 2 :

I found some errors :

$formatedAddress = array();
$divisor = 3;
$key = 0;
$counter =1;
foreach ($addressParts as $part)
{
    if(!empty($part)){

    $formatedAddress[$key][] = $part;

    if($counter != $divisor){
       $counter++;
    }else{
        $counter = 1;
        $key++;
    }
    }
}
Mirza Selimovic
  • 1,669
  • 15
  • 18
  • For now, you can assume it will always be 3. To make it vary will be easy. – rockstardev Oct 30 '13 at 14:12
  • The most recent edit (which I assume is the best one) does not provide the output array that the OP requires. Not only is the general structure incorrect, the OP wants to implode the values into comma-separated values. Proof: https://3v4l.org/GRva8 – mickmackusa Nov 03 '18 at 13:23
0
$source_array = array(
  'item 1',
  'item 2',
  'item 3',
  'item 4',
  'item 5',
  'item 6',
  'item 7',
  'item 8',
  'item 9',
  'item 10',
);

function reduce_array($input, $first_count = 3, $count = 2)
{
    $array_slice = array(array_slice($input, 0, $first_count));

    $array_chunk = array_chunk(array_slice($input, $first_count), $count);

    return array_merge($array_slice, $array_chunk);
}

$reduce_array = reduce_array($source_array); 

var_dump($reduce_array);

foreach($reduce_array as $array)
{
    var_dump(implode(',', $array));
}


Output:


array(5) {
  [0]=>
  array(3) {
    [0]=>
    string(6) "item 1"
    [1]=>
    string(6) "item 2"
    [2]=>
    string(6) "item 3"
  }
  [1]=>
  array(2) {
    [0]=>
    string(6) "item 4"
    [1]=>
    string(6) "item 5"
  }
  [2]=>
  array(2) {
    [0]=>
    string(6) "item 6"
    [1]=>
    string(6) "item 7"
  }
  [3]=>
  array(2) {
    [0]=>
    string(6) "item 8"
    [1]=>
    string(6) "item 9"
  }
  [4]=>
  array(1) {
    [0]=>
    string(7) "item 10"
  }
}
string(20) "item 1,item 2,item 3"
string(13) "item 4,item 5"
string(13) "item 6,item 7"
string(13) "item 8,item 9"
string(7) "item 10"
Oleg Ozimok
  • 259
  • 2
  • 4
0
<?php

    $sample_array = array('item 1','item 2', 'itme 3','item 4', 'item 5','item 6','item 7', 'item 8','item 9','item 10',
        'item 11','item 12','item 13','item 14','item 15','item 16');

    restructure($sample_array,3);        

          function restructure($array ,$size)
           {
            $i=0;       
            while($i<= count($array))
             {
                $j=$i;
                $count = 0;
                while ($count<$size)
                {
                    if($j<count($array))
                    {
                     echo $array[$j]. " ";
                     $j++;
                    }
                         $count++;
                 }
                 echo '<br>';
                 $i=$i+$size;

             }

           }

    ?>

    <?php
Eskinder
  • 1,369
  • 1
  • 14
  • 24
  • There is no JS tag in question – sectus Dec 04 '13 at 01:03
  • array.length() does not work. i and j should be $i and $j. The result is very bad, for 7 items it only returns 3. Did you try this code before posting it? – Robert Mikes Dec 06 '13 at 10:20
  • The first code i posted was just to give an idea but this one should work for you because i already tested it. – Eskinder Dec 06 '13 at 22:09
  • This answer does not provide the OP's desired output. This answer limits the number of comma-separated values per group to 3 and that's what is asked for. Proof: https://3v4l.org/Qqvua – mickmackusa Nov 03 '18 at 13:52
0

Try this out:

function array_group($array, $X){
    $extra = count($array)%$X;
    $pre = array_splice($array, 0, $extra);
    $post = array_chunk($array, count($array)/$X);

    $post[0] = array_merge($pre, $post[0]);

    foreach ($post as &$key) {
        $key = implode(', ', $key);
    }
    return $post;
}
Amit Kriplani
  • 682
  • 5
  • 12
  • Wrong value with 8 items. – sectus Dec 04 '13 at 00:21
  • This answer does not provide the desired output when the array contains 7 elements and the maximum rows is 4. Proof: https://3v4l.org/iQPrm It also doesn't work when the number of elements is 8 and the "row" limit is 3. Proof: https://3v4l.org/vOkef – mickmackusa Nov 03 '18 at 13:27
0

Is that ok?

$items = array('item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7');
array_unshift($items, '');
$chunkitems = array_chunk($items, 2);
$newitems = array_map(function($v){return implode(', ', $v);}, $chunkitems);
$first = ltrim(implode(', ', array(array_shift($newitems), array_shift($newitems))),', ');
array_unshift($newitems, $first);
print_r($newitems);
sectus
  • 15,605
  • 5
  • 55
  • 97
jingyu
  • 115
  • 2
  • 13
  • No, count of array result must be equal 3. Try your code with another array. – sectus Dec 05 '13 at 07:33
  • This answer does not limit the number of "rows" as required by the OP. Proof: https://3v4l.org/Ku9XZ (I also find the logic difficult to follow and I have no idea if it is built in a way that is customizable in case the row limit needs to be changed.) – mickmackusa Nov 03 '18 at 13:19
0

This seems pretty succinct:

function ReduceArray($input,$length)
{
    $count = count($input);

    // fill new array with new number of empty elements
    $newArray = array_fill(0,ceil($count/$length),"");

    for( $i = $count; $i > 0; $i--)
    {   
        // calculate index in new array to insert item
        $index = ceil($i / $length)-1;

        // we need a comma separator in this position if the array is empty
        $sep = ($newArray[$index] != "" ? "," : "");

        // insert into new array
        $newArray[$index] = array_pop($input) . $sep . $newArray[$index] ;
    }

    return $newArray;
}
mjhinch
  • 771
  • 4
  • 8
  • This answer breaks when there are 6 elements in the array and the number of output elements is set to 4. Proof: https://3v4l.org/kf06W – mickmackusa Nov 03 '18 at 13:45
0

Here's an adaptation from an answer of mine on another very, very similar question (near duplicate).

Code: (Demo)

function custom_chunk($array, $maxrows) {
    $result = [];
    $size = sizeof($array);
    $columns = ceil($size / $maxrows);
    $fullrows = $size - ($columns - 1) * $maxrows;

    for ($i = 0; $i < $maxrows; ++$i) {
        $result[] = implode(', ', array_splice($array, 0, ($i < $fullrows ? $columns : $columns - 1)));
    }
    return $result;
}

$data = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6', 'Item 7'];

var_export(custom_chunk($data, 3));

Output:

array (
  0 => 'Item 1, Item 2, Item 3',
  1 => 'Item 4, Item 5',
  2 => 'Item 6, Item 7',
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
-1

Another one cent, maybe. :-)

<?php

function restructure($x,$slots)
{
  $result=array();
  $count=count($x);

  $least=(int)($count/$slots);
  $excess=$count-$least*$slots;

  for($i=0;$i<($least+1)*$excess;$i+=$least+1)
    array_push($result,implode(", ",array_slice($x,$i,$least+1)));
  for(;$i<$count;$i+=$least)
    array_push($result,implode(", ",array_slice($x,$i,$least)));
  return $result;
}

if (PHP_SAPI==='cli') {

  $x=array(
  "item 1",
  "item 2",
  "item 3",
  "item 4",
  "item 5",
  "item 6",
  "item 7",
  );

  print_r(restructure($x,3));

}

Which makes:

Array
(
    [0] => item 1, item 2, item 3
    [1] => item 4, item 5
    [2] => item 6, item 7
)

Edit: fixed the bug @mickmackusa pointed out. See https://3v4l.org/hIVfb

shr
  • 525
  • 4
  • 7
  • This answer breaks when there are 7 elements in the array and the row count is set to 5. Proof: https://3v4l.org/Nt8VL – mickmackusa Nov 03 '18 at 13:06
-1

Great Question +1 for it. I have created solution more dynamically as.

  1. As the user input can be any number from 1.....n
  2. The Minimum Grouping of Members to be described or changed
  3. The Maximum Grouping of Members to be described or can be changed any time

This would be an edge upon other to change things accordingly:

<?

//can n number of array items
$items = array('item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7');

//counting of total number of items
$total_items = count($items);

//item to find in array
$find_item = "item7";

//first item found key number
$key = array_search($find_item, $items);

//Splitting into two

//array with last found as item
$temp_output1 = array_slice($items, 0, $key+1); 

//items left
$temp_output2 = array_slice($items, $key+1, $total_items); 

//minimum number items to group
$minimum_group=2;

//Maximum Number of Grouped items
$maximum_group=4;

if ( $temp_output1 ) {
    //sending to group accordingly
    $output1 = do_slicing($temp_output1, $minimum_group, $maximum_group);
    print_r($output1);
}

if ( $temp_output2 ) {
    //sending to group accordingly
    $output2 = do_slicing($temp_output2, $minimum_group, $maximum_group);
    print_r($output2);
}


function do_slicing($temp_output = array(), $minimum_group, $maximum_group){
    $count = count($temp_output);

    //array is equal to min grouping
    if ( $count == $minimum_group){
        //Group them and return
        return by_grouping($temp_output, $minimum_group);
    }elseif ($count == $maximum_group){
        //if items equal to maximum then group them and return
        return by_grouping($temp_output, $maximum_group);
    }elseif ( ($count > $minimum_group) and ($count < $maximum_group)){
        return by_grouping($temp_output, count($temp_output));
    }elseif ($count == 1) {
        //if item is 1 print error or return from here
        return $temp_output;
    }else{
        //calculate the total number of groups available
        $item_slice_count = intval($count/$maximum_group);

        //Split them as grouped members
        $temp_slice = array_slice($temp_output, 0, 
                    ($item_slice_count*$maximum_group));

        //non group members
        $temp_slice2 = array_slice($temp_output, 
                    ($item_slice_count*$maximum_group), $count);

        //if there is no non group members
        if ( !$temp_slice2 ) {
            //combine them and return according to maximum grouping
            return by_grouping($temp_slice, $maximum_group);
        }else{
            if ( 
                (count($temp_slice2) === $minimum_group) or 
                (
                  (count($temp_slice2) > $minimum_group) and  
                  (count($temp_slice2) < 
                   $maximum_group)
                 ) 
             ){
                //if count of slice2 equal to minimum group then
                $a=by_grouping($temp_slice, $maximum_group);
                if ( 
                  (count($temp_slice2) > $minimum_group) and  
                  (count($temp_slice2) < 
                   $maximum_group)
                 ){
                    $b=by_grouping($temp_slice2, count($temp_slice2));
                }else{
                    $b=by_grouping($temp_slice2, $minimum_group);
                }
                return array_merge($a, $b);
            }elseif( count($temp_slice2) < $minimum_group ) {
                //if less then minimum group then
                //if total count is divisible with minimum group
                if ( ($count % $minimum_group ) == 0 ){
                    //return after clubbing according minimum group
                    return by_grouping($temp_output, $minimum_group);
                }else{
                    //Where last group is not equal to minimum group

                    //Minimum more needed to complete last slice
                    $minimum_needed = $minimum_group - count($temp_slice2);

                    //slice1 become
                    $slice1 = array_slice($temp_output, 0, (
                               ($item_slice_count-1)*$maximum_group));

                    //slice2 would be
                    $slice2 = array_slice($temp_output, (
                                      ($item_slice_count-1)*$maximum_group),
                                      ($maximum_group-$minimum_needed));

                    //slice 3 then
                    $slice3 = array_slice($temp_output, ( 
                               (($item_slice_count-1)*$maximum_group) + 
                              ($maximum_group-$minimum_needed)),
                               $count);

                    //if slice2 is greater or equal to minimum group then
                    if ( $slice2>=$minimum_group) {
                        $a=by_grouping($slice1, $maximum_group);
                        $b=array_merge($a, by_grouping($slice2,
                                      count($slice2)));
                        $c=by_grouping($slice3, $minimum_group);
                        return array_merge($b, $c);
                    }else{
                        die("This Situation doesn't reached any time");
                    }
                }
            }
        }
    }
}

//Grouping of members according to slices provided
function by_grouping($temp_slice, $group){
    $return = array();
    $temp = array_chunk($temp_slice, $group);
    foreach($temp as $a){
        $return[] = implode(', ', $a);
    }
    return $return;
}

?>

DEMO CODE

Vineet1982
  • 7,730
  • 4
  • 32
  • 67
  • This is the right answer to the wrong question. At no point did the OP ask to search for a specific value. Because this page is already overloaded with answers, it would make this page more valuable if the only answers on page did the one thing that the OP asked for. – mickmackusa Nov 03 '18 at 13:03