自定义array_chunk以尽可能优先填充列


Custom array_chunk to prioritize filling columns as much as possible

我有一个数组,里面可以包含多个项目,例如:

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

我需要最快的方法来重组这个数组,以便它最多有 X 个项目。因此,如果我说 X 是 3,则生成的数组必须是:

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

或者,如果它有 7 个项目,它将是:

Item 1 , Item 2, Item 3,
Item 4, Item 5, 
Item 6, Item 7
最简单的

方法是什么?

我从这个开始,但似乎真的必须有一个更简单的方法:

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

此外,这不起作用,因为您最终会得到以下内容:

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

。这是错误的。有什么想法吗?

更新

如果我从以下方面开始:

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

我想以以下方式结束:

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

有意义?

此函数根据您的示例将元素组合到一个新数组中。它处理任意数量的输入元素。

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);

输出

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

根据给定的输出编辑"固定"输出。

编辑 请参阅注释 对于 9 个元素,最多针对 12 个元素进行了测试。谢谢教派

对于每个组,计算第一个元素和组长度的偏移量,从输入数组中复制该切片。

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
  )
);

输出:

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"
}

函数 array_group_elements() 循环遍历$groups(3 次),而不是$array(7 次)。

我有两个不同方法的函数。

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;
    }

要获得字符串数组的结果,您只需array_map它。

$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));

演示

文档的相对链接:array_splice、array_chunk array_merge

附言最短的解决方案


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

演示

我想出了以下算法,该算法可以在每行中保持拟合正确数量的项目; 在每次迭代中,我们将剩余行数与剩余项的划分四舍五入:

$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);

演示

<?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
    )
 */

对于每个组,您需要计算每个段中适合的最大项目数,因此我使用 ceil(); 函数始终向上舍入。

输入:

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

功能:

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');

输出:

Array
(
    [0] => item1, item2, item3
    [1] => item4, item5
    [2] => item6, item7
)

代码:

$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);

输出:

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

我想知道问题描述是否不正确,所需的结果实际上是一个多维数组,而不是串联字符串的数组。在这种情况下,只需删除对内爆的调用:

$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);

输出为:

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

适用于任意数量的项目。 并更改任意数量的列,只需更改$x值即可。

<?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);
    ?>

另一种算法,因为据我了解,问题是就地重构数组,因此在不创建临时数组时可以实现最佳效率。

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

测试用例 #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>';
?>

输出:

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

测试用例 #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>';
?>

输出:

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

如果您对二维数组感到满意,并且很高兴最后一行可以比其他所有行为小得多,这里有一行:

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

同样的事情,将每个片段合并成字符串:

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;
}

输出三个测试,包括 7、8 和 9 个项目:

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 ) 

测试:

$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>';
}

这将与您的示例完全匹配:

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;
}

结果:

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 ) 

只是为了好玩,这里有一个使用递归的解决方案:

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;
}

对不起,这应该是一个评论,但我不能做评论,也许是因为我的声誉......

当你有一个包含 7 个元素的数组时,你知道你会把数组除以 3 还是你必须找到除数?

编辑 1像这样的东西?:

$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++;
        }
    }
}

编辑 2 :

我发现了一些错误:

$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++;
    }
    }
}
$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"

试试这个:

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;
}

这似乎很简洁:

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;
}
可以

吗?

$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);
<?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
这是我对

另一个非常非常相似的问题(几乎重复)的回答的改编。

代码:(演示)

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));

输出:

array (
  0 => 'Item 1, Item 2, Item 3',
  1 => 'Item 4, Item 5',
  2 => 'Item 6, Item 7',
)

很好的问题 +1 对它。我已经更动态地创建了解决方案。

  1. 由于用户输入可以是 1 中的任何数字.....n
  2. 要描述或更改的成员的最小分组
  3. 要描述或可以随时更改的最大成员分组

这将是对其他人进行相应更改的优势:

<?
//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;
}
?>

演示代码

也许还有一分钱。

<?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));
}

这使得:

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

编辑:修复了@mickmackusa指出的错误。见 https://3v4l.org/hIVfb