array_chunk()函数的妙用——分而治之
小激动
本来这篇博客应该是留到递归系列博客的第七诫写的,但是难以抑制自己第一次成功地用【分而治之】解决问题的小激动,以及怕到了那个时候忘了很多细节,就先写下这篇博客了。
前言:
前两天在学 N+1 问题的时,看到了解决 N+1 的问题的方法中出现了‘预加载’这个词,感觉这个词在学习 PDO 的时候遇见过,就去看了一下 PDO 相关的内容。 发现是记错了,把‘预处理’记成了‘预加载’。不过 PDO 这块好久没看了,刚好我想用原生php复现 N+1 问题,需要填充数据,就想着复习一下 PDO。不过 PDO 不是今天这篇博客的主题,主题是当我使用 PDO 的预处理实现一次性向数据库中添加多条数据时,我该怎么处理接收到的数据?
问题描述:
假如我想向topics表中一次性添加100条数据,接收到的数据如下:
$data = [
['id'=>1,'title'=>'sdf','body'=>'fdf','user_id'=>1,'category_id'=>3],
['id'=>2,'title'=>'gdf','body'=>'dfdf','user_id'=>6,'category_id'=>1],
...
['id'=>100,'title'=>'fgh','body'=>'hd','user_id'=>4,'category_id'=>1],
]
- 准备SQL语句:
INSERT INTO topics(`title`,`body`,`user_id`,`category_id`) VALUES (?,?,?,?),(?,?,?,?),...,(?,?,?,?);
- 向表中添加数据:
我接收到的是一个二维数组,但这并不是我想要的,我想要的是该数组的每个单元的array_values组成的一维数组,这样我才能一次性添加多条数据。什么意思呢?举个例子:
假如我要向该表中添加两条数据,接收到的数据如下:
我希望得到的数据如下:$data = [ ['title'=>'a','body'=>'b','user_id'=>2,'category_id'=>3], ['title'=>'c','body'=>'d','user_id'=>9,'category_id'=>8], ];
$insertData = ['a','b',2,3,'c','d',9,8]; // 'a','b',2,3是$data第一个单元的array_values,'c','d',9,8是$data第二个单元的array_values。
问题建模:
输入是一个二维数组,数组的每个单元都是关联数组,输出是由每个单元的array_values组成的一维数组。示例如下:
输入:
[
['id'=>1,'name'=>'a'],
['id'=>2,'name'=>'b'],
['id'=>3,'name'=>'c'],
['id'=>4,'name'=>'d'],
]
输出:
[1,'a',2,'b',3,'c',4,'d']
具体实现:
1. 最简单的方法:
利用foreach遍历,或者根据我前面的博客中提到的递归前四诫,利用递归实现。但是时间复杂度有点感人,暂且不表。今天要讲的就是利用分而治之来解决这个问题。
2. 分而治之:
2.1 首先要问的是:能不能把这个大问题分解成若干个子问题来处理?
答案是可以的。例如:就算利用foreach遍历,处理规模为100的二维数组和处理规模为50的二维数组的方法也是一样的。
2.2 其次要问的是:该怎么把这个大问题拆解成若干个子问题?
答案是利用array_chunk()函数。
- 语法:
array_chunk(array $array, int $length, bool $preserve_keys = false): array - 参数:
- array:需要操作的数组。
- length:每个数组的单元数目。
- 功能:
将一个数组分割成多个数组,其中每个数组的单元数目由 length 决定。最后一个数组的单元数目可能会少于 length 个。
举个生活上的例子就好理解了:
想象一下,有5个球排成一行,每隔两个球放一块挡板,就会分成 2 2 1。【5个球】就是数组的长度,【每隔n个球放一块挡板】就是参数 length,在这里 length 为2。最后一堆球的个数为1,小于2,符合【最后一个数组的单元数目可能会少于 length 个】。
array_chunk 函数介绍完毕,就看看 array_chunk 在分而治之中的应用吧。
想要分而治之,只需要将输入的二维数组分成两个,然后再分别对其进行递归调用。
代码片段如下:
$re = array_chunk($data, ceil(count($data)/2));
$part1 = getArrValues($re[0]);
$part2 = getArrValues($re[1]);
注意:需要对count($data)/2)
进行向上取整,即ceil(count($data)/2))
。
就拿php文档中的例子来说吧:
输入:$input_array = array(‘a’, ‘b’, ‘c’, ‘d’, ‘e’);
如果向下取整:
print_r(array_chunk($input_array, floor(count($input_array)/2)));
// 输出:[[0]=>['a','b'],[1]=>['c','d'],[3]=>['e']]
分析:$length = floor(count($input_array)/2))) = 2; 五个球,每隔两个球放一块挡板,就会分成三堆。所以分成了三个数组。
向上取整得到的输出如下:
print_r(array_chunk($input_array, ceil(count($input_array)/2)));
[[0]=>['a','b','c'],[1]=>['d','e']]
分析:$length = ceil(count($input_array)/2))) = 3;
五个球,每隔三个球放一块挡板,就会分成两堆。所以就得到了两个数组。
2.3 最后要问的是:什么时候终止递归?换句话说:我们要处理的最简单的问题是什么?
最简单的问题就是输入的二维数组中只有一个单元。
例如:
$data = [
['id'=>1,'name'='a']
]
怎么处理?
if (count($data)==1) {
return array_values($data[0]);
}
分析到这里,基本没什么问题了,再结合递归十诫之第二诫:使用array_merge函数来构建数组,就能写出完整的代码了。
完整代码如下:
function getArrValues($data)
{
if (count($data)==1) {
return array_values($data[0]);
}
$re = array_chunk($data, ceil(count($data)/2));
$part1 = getArrValues($re[0]);
$part2 = getArrValues($re[1]);
return array_merge($part1, $part2);
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
楼主 50-70w数据要怎么处理,老是内存溢出‘..’