# 常用数组函数大全
[toc]
## 1. 数组基础
### 1.1 概述与分类
- 数组是典型的复合类型数据,它的值是由零个或多个**键值对**构成
- 数组中的每一个"键值对", 称之为**数组成员** 或者 **数组元素**
- 根据**键**的类名,可以将数组分为二类:
| 序号 | 分类 | 描述 |
| ---- | -------- | ------------------------------- |
| 1 | 索引数组 | 键名默认是从 0 开始递增的正整数 |
| 2 | 关联数组 | 键名是由语义化的字符串组成 |
> 索引数组键名可使用数字字符串,所以,索引数组也可视为关联数组的一个子集
```php
// 1. 数组分类
// 1.1 索引数组
$goods = [0=>'A1060', 1=>'华为笔记本电脑', 2=>'MateBook X Pro', 3=>9999];
printf('<pre>%s</pre>', print_r($goods,true));
// 如果键名是从0开始递增的正整数,可以省略键名
$goods = ['A1060', '华为笔记本电脑', 'MateBook X Pro', 9999];
printf('<pre>%s</pre>', print_r($goods,true));
// 键名可以不连续,可以是负数,甚至可以是小数
$goods = ['A1060', 5=>'华为笔记本电脑', 3.14=>'MateBook X Pro', -88=>9999];
printf('<pre>%s</pre>', print_r($goods,true));
// 打印键名时,只会显示键名的整数部分, 即小数键名会自动取整,访问时用原始键名或取整键名都可以
echo "{$goods[3.14]} --- {$goods[3]}<br>";
// 访问全部数组成员
// 1.2 关联数组
// 键名为非数字的字符串,例如将上面的索引数组,用以下方式定义,显然比纯数字的索引要直观得多
$goods = ['id'=>'A1060', 'name'=>'华为笔记本电脑', 'model'=>'MateBook X Pro', 'price'=>9999];
printf('<pre>%s</pre>', print_r($goods,true));
// 为什么特别强调是非数字字符串呢?因为索引数组的数字键名也可以这样表示
$goods = ['0'=>'A1060', '1'=>'华为笔记本电脑', '2'=>'MateBook X Pro', '3'=>9999];
printf('<pre>%s</pre>', print_r($goods,true));
// 访问的时候,可以使用数字键名,也可以使用字符串数字键名
echo "{$goods[1]} --- {$goods['1']}<br>";
// 所以, 索引数组,也可以理解为键名是数字字符串的关联数组
// 从这个角度看, 索引数组,可以视为"关联数组"的一个子集
// 因此, 数字键名与字符串键名在同一个数组中出现,其实也是合法的
$goods = [3=>'A1060', 'name'=>'华为笔记本电脑', 10=>'MateBook X Pro', 'price'=>9999];
printf('<pre>%s</pre>', print_r($goods,true));
// 如果省略数字键名部分, 默认使用从0开始递增的正整数为元素编号
$goods = ['A1060', 'name'=>'华为笔记本电脑', 'MateBook X Pro', 'price'=>9999];
printf('<pre>%s</pre>', print_r($goods,true));
```
### 1.2 定义与访问
| 序号 | 定义 | 描述 |
| ---- | ------------- | ------------------------------ |
| 1 | `array()`函数 | 传统定义方式,已淘汰或不再推荐 |
| 2 | `[...]`结构 | php5.4+支持, 语法简洁,推荐使用 |
> 访问遵循按键名访问的规则, 多维数组与不例外
```php
# 2. 数组的定义与访问
// array()函数定义数组
$staff1 = array('1010', '八戒','bj@php.cn');
$staff2 = array('id'=>'1010', 'name'=>'八戒', 'email'=>'bj@php.cn');
printf('<pre>%s%s</pre>', print_r($staff1,true),print_r($staff2,true));
// 方括号[]简化定义: php 5.4+,与JavaScript类似, 推荐使用
$staff3 = ['1010', '八戒', 'bj@php.cn'];
$staff4 =['id'=>'1010', 'name'=>'八戒', 'email'=>'bj@php.cn'];
printf('<pre>%s%s</pre>', print_r($staff3,true),print_r($staff4,true));
// 使用方括号,还可以逐个定义数组成员
$staff5['id'] = '1020';
$staff5['name'] = '悟空';
$staff5['email'] = 'wk@php.cn';
printf('<pre>%s</pre>', print_r($staff5,true));
// 如果是逐个定义索引数组成员,如果省略键名,则键名默认从0开始递增
$staff6[] = '1030';
$staff6[] = '唐僧';
$staff6[] = 'ts@php.cn';
printf('<pre>%s</pre>', print_r($staff6,true));
// 数组成员是按键名访问的
// 访问索引数组成员
echo "$staff6[1] 的邮箱是: $staff6[2]<br>";
// 访问关联数组成员
// 双引号中的数组成员变量,键名部分使用了单引号, 所以一定要给数组变量加上大括号定界符
echo "{$staff5['name']} 的邮箱是: {$staff5['email']}<br>";
// 数组成员的类型不受限制,可以是字面量,可以是变量,可以是对象等
$age = 30;
$staff7 =['id'=>'1010', 'name'=>'八戒', 'age'=>$age, 'email'=>'bj@php.cn'];
printf('<pre>%s</pre>', print_r($staff7,true));
// 如果数组成员的类型仍是一个数组,则构成多维数组
// 多维数组中最常用的是二维数组,二维数组在php操作数据库中非常重要
$users = [
0=>['id'=>'101', 'name'=>'玉帝', 'age'=>88],
1=>['id'=>'102', 'name'=>'王母', 'age'=>78],
2=>['id'=>'101', 'name'=>'如来', 'age'=>68],
];
printf('<pre>%s</pre>', print_r($users,true));
// 访问二维数组成员,与访问一维数组成员类似
// 先定位到某一个成员,再访问它的全部数据, 下例返回的是"如来"的数据
printf('<pre>%s</pre>', print_r($users[2],true));
// 查看如来的年龄
echo $users[2]['age'];
```
### 1.3 遍历
| 序号 | 方式 | 描述 |
| ---- | -------- | ----------------- |
| 1 | 逐个遍历 | 使用数组内部指针 |
| 2 | 循环遍历 | 使用`foreach`结构 |
数组内部指针函数:
| 序号 | 函数 | 描述 |
| ---- | --------- | -------------------------------- |
| 1 | `current` | 获取当前元素的值 |
| 2 | `key` | 获取当前元素的键名 |
| 3 | `next` | 将数组中的内部指针向前移动一位 |
| 4 | `prev` | 将数组的内部指针倒回一位 |
| 5 | `end` | 将数组的内部指针指向最后一个单元 |
| 6 | `reset` | 将数组的内部指针指向第一个单元 |
`foreach`遍历:
| 序号 | 语法 | 描述 |
| ---- | -------------------------------- | ------------ |
| 1 | `foreach ($arr as $value)` | 遍历"值" |
| 2 | `foreach ($arr as $key=>$value)` | 遍历"键与值" |
| 3 | `foreach ($arr as list(...))` | 解构遍历 |
> foreach 不支持 @ 抑制错误
```php
# 3. 数组遍历
// 数据遍历通常与数组元素的处理结合在一起的,与简单的打印输出不一样
// 数组元素的处理, 最常用的就是数据的格式化
// 3.1 逐个遍历: 数组内部指针
// 如果没有对数组进行过遍历操作,则数组内部指针默认停留头部,即指向第一个元素
$stu = ['id' => '1020', 'name' => '金莲', 'age' => 18, 'course' => 'php', 'grade' => 68];
// 获取当前数据成员的键名与值
// current(): 当前成员值, key(): 当前成员的键名
printf('[%s] => %s<br>', key($stu), current($stu));
// next():移动指针,指向下一个数组成员
next($stu);
printf('[%s] => %s<br>', key($stu), current($stu));
// 再次移动
next($stu);
printf('[%s] => %s<br>', key($stu), current($stu));
// 如果要访问最后一个成员,是否一直next()下去吗?当然不用,可以直接定位到数组末尾
end($stu);
printf('[%s] => %s<br>', key($stu), current($stu));
// prev(): 向前移动指针,指向当前成员的前一个前面
prev($stu);
printf('[%s] => %s<br>', key($stu), current($stu));
// 同样,如果要从当前位置直接跳转到第一个成员,也不用逐级返回,可以重置数组指针即可
// 数组指针重置后, 会自动指向第一个成员
reset($stu);
printf('[%s] => %s<br>', key($stu), current($stu));
echo '<hr>';
// 3.2 自动遍历: 循环实现
// 你可能会很自然的想到,使用while,配合数组内部指针来遍历,咱们试试看
reset($stu);
while (true) {
printf('[%s] => %s<br>', key($stu), current($stu));
if (next($stu)) {
continue;
} else {
break;
}
}
echo '<hr>';
// 看上去似乎正常,其实并非如此,这是假定数组所有元素都是true的前提下
// 如果有数组成员值为false,或者可转为false的值,如null,就会提前结束遍历
$stu = ['id' => '1020', 'name' => '金莲', 'age' => null, 'course' => 'php', 'grade' => 68];
reset($stu);
while (true) {
printf('[%s] => %s<br>', key($stu), current($stu));
if (next($stu)) {
continue;
} else {
break;
}
}
// age后面的二个成员将不会打印输出
echo '<hr>';
// 既然while循环不能遍历, 那么for循环是否可以呢?
// 看到for循环,首先会想到遍因索引数组,这是必然的
$arr = [100, 'a', 89, true, [1, 2, 3]];
for ($i = 0; $i < count($stu); $i++) {
// 数组成员也可能仍然是数组,所以使用了is_array()判断一下,针对不同类型使用不同方式输出
echo is_array($arr[$i]) ? print_r($arr[$i], true) : $arr[$i], '<br>';
}
echo '<hr>';
// 看上去,for循环工作得很好,那么for循环能否正确遍历关联数组呢?
reset($stu);
for ($i = 0; $i < count($stu); $i++) {
printf('[%s] => %s<br>', key($stu), current($stu));
next($stu);
}
// 可以看出, for循环可以正常输出关联数组,并且能正确识别出null/false元素
// 这是因为它是根据数组成员数量做为循环条件的, 并非根据当前数组指针是否指向空元素来判断
// 尽管for循环可以遍历数组, 但仍然不方便,特别是遍历关联数组时,循环变量只是摆设,并且必须手工移到数组指针
// 针对数组类型,PHP预定义了foreach语法结构,专用于遍历数组类型的数据
echo '<hr>';
foreach ($stu as $key => $value) {
printf('[%s] => %s<br>', $key, $value);
}
// 对于索引数组,我们通常只关注值,可以省略循环变量的键名$key
echo '<hr>';
foreach ($arr as $value) {
echo is_array($value) ? print_r($value, true) : $value, '<br>';
}
echo '<hr>';
// foreach()在遍历二维数组时特别方便,以下用二维数组模拟数据表查询结构
$users = [
0 => ['id' => '101', 'name' => '玉帝', 'age' => 88],
1 => ['id' => '102', 'name' => '王母', 'age' => 78],
2 => ['id' => '101', 'name' => '如来', 'age' => 68],
];
// 索引是默认值,可以省略
$users = [
['id' => '101', 'name' => '玉帝', 'age' => 88],
['id' => '102', 'name' => '王母', 'age' => 78],
['id' => '101', 'name' => '如来', 'age' => 68],
];
// 也可将多维数组写在一行定义
// 前面可能定义过$users数组,为防止后面数据被追加,所以先清空
// 第一行必须加上$users = []; 清空该数组
$users = [];
$users[] = ['id' => '101', 'name' => '玉帝', 'age' => 88];
$users[] = ['id' => '102', 'name' => '王母', 'age' => 78];
$users[] = ['id' => '101', 'name' => '如来', 'age' => 68];
print_r($users);
foreach ($users as $user) {
foreach ($user as $key => $value) {
echo "$key => $value <br>";
}
echo '<hr>';
}
// 更多场景下, 我们并不会使用双重循环来输出二维数组
foreach ($users as $user) {
printf('id=%s, name=%s, age=%s<br>', $user['id'], $user['name'], $user['age']);
}
echo '<hr>';
// 如果二维数组中字段并不多,可以使用list()进行数组解包,类似于ES6中的解构赋值
// 提示: 在foreach中使用list()解构数组, 要求 PHP5.5+
foreach ($users as list('id' => $id, 'name' => $name, 'age' => $age)) {
printf('id=%s, name=%s, age=%s<br>', $id, $name, $age);
}
echo '<hr>';
// 对于索引数组, list()解构更加方便
$staffs = null;
$staffs[] = ['2020', '八戒', 29];
$staffs[] = ['2021', '悟空', 39];
$staffs[] = ['2022', '唐僧', 49];
foreach ($staffs as list($id, $name, $age)) {
printf('id=%s, name=%s, age=%s<br>', $id, $name, $age);
}
echo '<hr>';
// list()中的参数数量可以少于数组元素数量,但不能多于
foreach ($staffs as list($id, $name)) {
printf('id=%s, name=%s<br>', $id, $name);
}
// list()参数过多,会报Notice消息级警告错误
foreach ($staffs as list($id, $name, $age, $email)) {
printf('id=%s, name=%s, email=%s<br>', $id, $name,$age, $email);
}
echo '<hr>';
// list()中允许嵌套list, 用于解析多维数组
// $staffs中添加一个字段, 表示js和php的成绩
$staffs = null;
$staffs[] = ['2020', '八戒', 29, [67, 88]];
$staffs[] = ['2021', '悟空', 39, [99, 31]];
$staffs[] = ['2022', '唐僧', 49, [56, 63]];
foreach ($staffs as list($id, $name, $age, list($js, $php))) {
printf('id=%s, name=%s, age=%s, js=%s, php=%s<br>', $id, $name,$age, $js, $php);
}
echo '<hr>';
// foreach()不仅仅可以遍历数组,还可以遍历对象属性
$obj = new stdClass();
$obj->name = 'admin';
$obj->email = 'admin@php.cn';
$obj->role = 'custom';
foreach ($obj as $prop=>$value) {
printf('%s => %s<br>',$prop, $value);
}
```
---
## 2. 数组函数
### 2.1. 键名相关
| 序号 | 函数 | 描述 |
| ---- | ------------------ | -------------------------- |
| 1 | `array_keys` | 获取所有键名组成的数组 |
| 2 | `array_key_exists` | 是否存在指定键名 |
| 3 | `array_key_last` | 获取最后一个键名 `php7.3+` |
| 4 | `array_key_first` | 获取第一个键名 `php7.3+` |
```php
// ## 键名相关函数
// array_keys: 获取所有键名组成的数组
$arr = ['id' => 1, 'username' => 'admin', 'email' => 'admin@php.cn'];
print_r(array_keys($arr));
echo '<hr>';
// array_key_exists: 是否存在指定键名
echo (array_key_exists('email', $arr) ? '存在' : '不存在') . '<hr>';
// array_key_last: 获取最后一个键名php7.3+
echo array_key_last($arr) . '<hr>';
// array_key_first: 获取第一个键名php7.3+
echo array_key_first($arr) . '<hr>';
```
---
### 2.2 与值相关
| 序号 | 函数 | 描述 |
| ---- | -------------- | -------------------------- |
| 1 | `array_values` | 返回数组中所有值组成的数组 |
| 2 | `in_array` | 检查数组中是否存在某个值 |
| 3 | `array_search` | 搜索指定的值,返回键名 |
| 4 | `array_unique` | 删除重复的值 |
```php
// ## 与值相关的数组函数
// array_values: 返回数组中所有值组成的数组
$arr = [3 => 10, 9 => 20, 0 => 'html', 'id' => 'css', 20 => 20, 30];
print_r($arr);
echo '<hr>';
print_r(array_values($arr));
echo '<hr>'; // 键名被打乱的数组进行重置
// in_array: 检查数组中是否存在某个值
echo (in_array('html', $arr) ? '存在' : '不存在') . '<br>';
// array_search: 搜索指定的值,返回键名
echo array_search('css', $arr);
echo '<br>';
echo $arr[array_search('css', $arr)] . '<br>';
// array_unique: 删除重复的值
$newArr = array_unique($arr);
print_r($newArr);
```
---
### 2.3 与统计相关
| 序号 | 函数 | 描述 |
| ---- | -------------------- | -------------------------- |
| 1 | `count` | 统计元素数量或对象属性数量 |
| 2 | `array_count_values` | 统计所有值的出现频率 |
```php
// 与统计相关
$arr = [10, 3, 5, 3, 10, 5, 7, 3, 10, 7, 7];
printf('<pre>%s</pre>', print_r($arr, true));
// count: 统计元素数量或对象属性数量
echo '数组元素数量: ' . count($arr);
// array_count_values: 统计所有值的出现频率
$res = array_count_values($arr);
printf('<pre>%s</pre>', print_r($res, true));
```
---
### 2.4 与计算相关
| 序号 | 函数 | 描述 |
| ---- | --------------- | ---------------------- |
| 1 | `array_sum` | 对数组中所有值求和 |
| 2 | `array_product` | 计算数组中所有值的乘积 |
```php
// 与计算相关
$arr = [10, 20, 30];
// array_sum: 对数组中所有值求和
echo array_sum($arr) . '<hr>';
// array_product: 计算数组中所有值的乘积
echo array_product($arr);
```
---
### 2.5 栈与队列
| 序号 | 函数 | 描述 |
| ---- | --------------- | ------------------------ |
| 1 | `array_push` | 从尾部添加一个或多个元素 |
| 2 | `array_pop` | 从尾部删除最后一个元素 |
| 3 | `array_unshift` | 从头部添加一个或多个元素 |
| 4 | `array_shift` | 从头部删除一个元素 |
```php
// 栈与队列
// 栈: 后进先出(LIFO): 最后进入的元素最先出来
// 队列: 先进先出(FIFO): 最先插入的数据最先出来
// 1. 栈操作: 尾部进行
$stack = [];
// 1.1. array_push: 进栈, 返回数量
echo array_push($stack, 10) . '<br>';
echo array_push($stack, 20, 30) . '<br>';
print_r($stack);
echo '<hr>';
// 1.2. array_pop: 出栈, 返回元素
echo array_pop($stack) . '<br>';
echo array_pop($stack) . '<br>';
echo array_pop($stack) . '<br>';
var_dump(array_pop($stack));
echo '<hr>';
// 2. array_unshift: 栈操作: 头部进行, 返回数量
$stack = [];
// 2.1 入栈
echo array_unshift($stack, 'one') . '<br>';
echo array_unshift($stack, 'two', 'three') . '<br>';
print_r($stack);
echo '<hr>';
// 2.2 array_shift: 出栈, 返回元素
echo array_shift($stack) . '<br>';
echo array_shift($stack) . '<br>';
echo array_shift($stack) . '<br>';
var_dump(array_pop($stack));
echo '<hr>';
// 3. 队列:
// 尾部添加, 头部出队 array_push()+array_shift()
// 进队
$queue = [];
echo array_push($queue, 10, 20, 30) . '<br>';
print_r($queue);
echo '<hr>';
//出队
echo array_shift($queue) . '<br>';
echo array_shift($queue) . '<br>';
echo array_shift($queue) . '<br>';
// 头部添加入队, 尾部出队 array_unshift() + array_pop()
$queue = [];
echo array_unshift($queue, 'one', 'two', 'three');
print_r($queue);
echo '<hr>';
echo array_pop($queue) . '<br>';
echo array_pop($queue) . '<br>';
echo array_pop($queue) . '<br>';
var_dump(array_pop($queue));
echo '<hr>';
```
---
### 2.6 排序
#### 2.6.1 对值排序
| 序号 | 函数 | 描述 |
| ---- | -------- | -------------------------- |
| 1 | `sort` | 按值升序排序, 索引重排 |
| 2 | `asort` | 按值升序排序, 索引保持不变 |
| 3 | `rsort` | 按值降序排序, 索引重排 |
| 4 | `arsort` | 按值降序排序, 索引保持不变 |
#### 2.6.2 对键排序
| 序号 | 函数 | 描述 |
| ---- | -------- | -------------- |
| 1 | `ksort` | 按键名升序排序 |
| 2 | `krsort` | 按键名降序排序 |
#### 2.6.3 自定义排序
| 序号 | 函数 | 描述 |
| ---- | -------- | -------------------------------- |
| 1 | `usort` | 自定义函数对值进行排序 |
| 2 | `uasort` | 自定义函数对值排序并保持索引不变 |
| 3 | `uksort` | 自定义函数对键名进行排序 |
#### 2.6.4 自然排序
| 序号 | 函数 | 描述 |
| ---- | ------------- | -------------------- |
| 1 | `natsort` | 支持数字型字符串排序 |
| 2 | `natcasesort` | 不区分大小写 |
#### 2.6.5 乱序反转
| 序号 | 函数 | 描述 |
| ---- | --------------- | ---------------------- |
| 1 | `shuffle` | 随机打乱一个数组的顺序 |
| 2 | `array_flip` | 交换数组中的键和值 |
| 3 | `array_reverse` | 反转一个数组 |
示例:
```php
// 排序: 参数是引用传值, 直接更新原始数组
// 1. 对值排序
$arr = [30, 4, 82, 15, 20, 'abc', 'hello', 2, 46];
printf('原始数组:<pre>%s</pre><hr>', print_r($arr, true));
// 1.1升序
// 升序: 索引重置
sort($arr);
printf('升序索引重置:<pre>%s</pre>', print_r($arr, true));
// 升序: 索引保持不变
// 数组还原, 便于观察排序结果
$arr = [30, 4, 82, 15, 20, 'abc', 'hello', 2, 46];
asort($arr);
printf('升序索引不变:<pre>%s</pre>', print_r($arr, true));
// 1.2 降序
// 降序: 索引重置
$arr = [30, 4, 82, 15, 20, 'abc', 'hello', 2, 46];
rsort($arr);
printf('降序索引重置:<pre>%s</pre>', print_r($arr, true));
// 降序: 索引保持不变
$arr = [30, 4, 82, 15, 20, 'abc', 'hello', 2, 46];
arsort($arr);
printf('降序索引不变:<pre>%s</pre>', print_r($arr, true));
echo '<hr>';
// 2. 对键排序
$arr = ['e' => 10, 'a' => 30, 'p' => 50];
// 2.1 按键名升序
ksort($arr);
printf('按键名升序:<pre>%s</pre>', print_r($arr, true));
// 2.2 按键名降序
$arr = ['e' => 10, 'a' => 30, 'p' => 50];
krsort($arr); // 降序
printf('按键名降序:<pre>%s</pre>', print_r($arr, true));
echo '<hr>';
// 3. 自定义排序
$arr = [90, 33, 4, 10, 2, 12];
// 3.1 自定义升序
usort($arr, function ($a, $b) {
return $a - $b;
});
printf('自定义升序:<pre>%s</pre>', print_r($arr, true));
// 3.2 自定义降序
$arr = [90, 33, 4, 10, 2, 12];
uasort($arr, function ($a, $b) {
return $a - $b;
});
printf('自定义升序且索引不变:<pre>%s</pre>', print_r($arr, true));
echo '<hr>';
// 4. 自然排序
$arr = ['img1.jpg', 'img5.jpg', 'img10.jpg', 'img8.jpg'];
sort($arr);
// 不能正确识别出字母数字字符串 "1" 和 "10" 之间的区别
printf('普通升序:<pre>%s</pre>', print_r($arr, true));
$arr = ['img1.jpg', 'img5.jpg', 'img10.jpg', 'img8.jpg'];
// 自然排序可以正确识别出字母数字字符串中的数值
natsort($arr);
printf('自然升序:<pre>%s</pre>', print_r($arr, true));
echo '<hr>';
// 5. 乱序反转
$arr = ['id' => 109, 'username' => 'peter', 'age' => 27, 'salary' => 99999];
// 随机打乱一个数组,引用传参, 会改变原数组
shuffle($arr);
printf('随机扰乱:<pre>%s</pre>', print_r($arr, true));
// 翻转一个数组,返回一个新数组,翻转是指第一个变成最后一个,依次类推
$arr = array_reverse($arr);
printf('翻转数组:<pre>%s</pre>', print_r($arr, true));
$arr = ['name' => 'admin', 'age' => 30, 'salary' => 8888];
// 注意键名的合法性,如果不是"integer"或"string"会有警告,且不会出现在结果中
$arr = ['name' => 'admin', 'age' => 30, 'salary' => false];
// 交换键值
$arr = array_flip($arr);
printf('交换键值:<pre>%s</pre>', print_r($arr, true));
```
---
### 2.7 查询与替换
| 序号 | 函数 | 描述 |
| ---- | ------------------------- | -------------------------------------- |
| 1 | `array_slice` | 从数组中取出一部分 |
| 2 | `array_splice` | 去掉数组中一部分并用其它值代替 |
| 3 | `array_rand` | 从数组中随机取出一个或多个元素的键名 |
| 4 | `array_column` | 获取多维数组中一列组成的新数组 |
| 5 | `array_replace` | 使用后面数组的值替换第一个数组的值 |
| 6 | `array_replace_recursive` | 使用传递的数组递归替换第一个数组的元素 |
| 7 | `array_intersect` | 计算数组的交集 |
| 8 | `array_intersect_assoc` | 返回数组交集,键名也做比较 |
| 9 | `array_diff` | 返回数组的差集 |
| 10 | `array_diff_assoc` | 返回数组差集,键名也做比较 |
示例:
```php
## 查询与替换
// 1. array_slice($arr,$offset,$length,$flag): 从数组中取出一部分
$stu = ['id' => 101, 'name' => '无忌', 'age' => 20, 'course' => 'php', 'grade' => 80];
// offset=0: 获取从索引0开始直到结束,即获取全部元素,这不是这个函数的主要使用场景
$res = array_slice($stu, 0);
// 获取前2个
$res = array_slice($stu, 0, 2);
// 从第2个开始获取3个
$res = array_slice($stu, 1, 3);
// 第2个参数支持负数,数组索引反序从-1开始,-3表示从倒数第3个
// 从倒数第3个开始,获取2个
$res = array_slice($stu, -3, 2);
// 第3个参数也支持负数,表示元素区间的结束索引(返回结果不包括结束索引对应的元素)
// 注意结果中并不包括索引-1对应元素'grade'
$res = array_slice($stu, 1, -1);
// 第4个参数是布尔值,默认为false,针对索引数组才有意义
// 将$stu转为索引数组, false表示, 获取到的部分元素,索引重置,即从0开始重排
$res = array_slice(array_values($stu), 1, -1, false);
// true: 表示保持元素在原始数组中的索引不变
$res = array_slice(array_values($stu), 1, -1, true);
printf('<pre>%s</pre>', print_r($res, true));
echo '<hr>';
// 2. array_splice(&$arr...): 去掉数组中一部分并用其它值代替,引用传参,会更新原数组,键名自动重置
// 该函数功能非常强大,具有数组元素的: 删除, 替换, 增加功能
$arr = [10, 28, 9, 33, 56, 21, 82, 47];
printf('原始数组元素: <pre>%s</pre>', print_r($arr, true));
// 2.1 删除: 指定起始索引和元素数量即可,从第2个元素开始删除2个,返回被删除的元素
// $res = array_splice($arr, 1,2);
// 2.2 替换: 传入第3个数组参数, 替换掉被删除的元素,如果数组元素数量与被删除元素数量相等,则可视为更新操作
// 为了便于观察结果,请将上一条array_splice()语句注释掉
// $res = array_splice($arr, 1,2, [888, 999]);
// 如果只替换一个元素,则第四个参数不需要以数组形式出现,单值即可
// $res = array_splice($arr, 1,2, 888);
// 2.3 增加: 如果第3个参数length的值为0,而又提供了第4个参数,表示不会有元素被删除,新元素会被插入到数组中
// $res = array_splice($arr, 1,0, [888, 999]);
// 第2与第3个参数也支持负数,含义与array_slice()一样
// 将从倒数第3个元素开始的2个元素,替换成888,999
// $res = array_splice($arr, -3,2, [888, 999]);
// 将从倒数第4个位置到倒数第2个位置之间的元素替换成888,999 (注意不包括-2位置的元素)
$res = array_splice($arr, -4, -2, [888, 999]);
printf('被删除的元素:<pre>%s</pre>', print_r($res, true));
printf('当前数组元素: <pre>%s</pre>', print_r($arr, true));
echo '<hr>';
// 3. array_rand: 从数组中随机取出一个或多个元素的键名,支持索引与关联数组
$arr = ['一等奖', '二等奖', '三等奖', '谢谢参与'];
// 如果只传入一个参数,即数组,则只返回一个随机的键名
$res = array_rand($arr);
printf('%s<br>', $res);
// 当然这个键名对用户来说意义不大,用户真正感兴趣的是键名对应的值
printf('[%s] => %s<br>', $res, $arr[$res]);
// 第二个参数是一个正整数,表示要取出的元素数量,返回一个键名组成的一维数组
// 例如,一次性随机取出三个,提高中奖率
$res = array_rand($arr, 3);
printf('<pre>%s</pre>', print_r($res, true));
// 遍历键名数组,查看对应的中奖情况
foreach ($res as $key) {
printf('%s<br>', $arr[$key]);
}
echo '<hr>';
// 4. array_column: 获取多维数组中一列组成的新数组
$arr = null;
$arr[] = ['id' => 101, 'name' => 'jack', 'age' => 20];
$arr[] = ['id' => 102, 'name' => 'mike', 'age' => 30];
$arr[] = ['id' => 103, 'name' => 'pony', 'age' => 40];
// 只指定返回数组的列名称, 默认返回列值组成的一维索引数组,键名为默认值
$res = array_column($arr, 'name');
// 为了更加精准描述结果,允许自定义返回数组的键名,键名须是原数组中除第2个参数之外键名
$res = array_column($arr, 'name', 'id');
printf('<pre>%s</pre>', print_r($res,true));
// 这个参数是数据表查询, 获取单个字符的值时非常有用
echo '<hr>';
// 5. array_replace: 使用后面数组的值替换第一个数组的值, 同名的键名会彼此覆盖,适合自定义配置参数
$arr = ['type'=>'mysql', 'host'=>'localhost', 'username'=>'root', 'password'=>'root'];
$arr1 = ['host'=>'127.0.0.1', 'username'=>'admin'];
$arr2 = ['username'=>'peter', 'password'=>'123456'];
$res =array_replace($arr, $arr1, $arr2);
printf('<pre>%s</pre>', print_r($res,true));
echo '<hr>';
// 6. array_intersect: 计算数组的交集
$arr1 = [10, 20, 30, 40, 'php'];
$arr2 = [1,'php', 20, 30, 5];
$arr3 = ['a','c', 'php', 30, 10, 20];
// 交集,即返回多个数组中, 都存在的元素
$res = array_intersect($arr1, $arr2, $arr3); // 20, 30, 'php'
printf('<pre>%s</pre>', print_r($res,true));
// array_intersect_assoc: 返回数组交集,键名也做比较,
echo '<hr>';
// 7. array_diff:返回数组的差集
// 从第一个参数$arr数组中, 去掉在$arr2, $arr3中存在的元素
// 可理解为减法: $arr1 - $arr2 - $arr3 ,最终剩下的是只存在于$arr1中的元素
$res = array_diff($arr1, $arr2, $arr3);
printf('<pre>%s</pre>', print_r($res,true)); // 只剩下40
// array_diff_assoc: 返回数组差集,键名也做比较,请同学位自行测试
```
---
### 2.8 分割与合并
| 序号 | 函数 | 描述 |
| ---- | --------------- | -------------------------------- |
| 1 | `array_combine` | 通过合并两个数组来创建一个新数组 |
| 2 | `array_merge` | 把一个或多个数组合并为一个数组 |
| 3 | `array_chunk` | 将一个数组分割成多个子数组 |
示例:
```php
// 分割与合并
// 1. array_combine: 通过合并两个数组(一个是"键数组",一个是"值数组")来创建一个新数组
$keys = ['type', 'host', 'dbname', 'username', 'password'];
$values = ['mysql', 'localhost', 'phpedu', 'root', 'root'];
$res = array_combine($keys, $values);
printf('<pre>%s</pre>', print_r($res,true));
echo '<hr>';
// 2. array_merge($arr1, $arr2,...): 把一个或多个数组合并为一个数组
// 这个功能经常被用在项目配置上, 使用用户自定义配置项覆盖项目默认配置顶
$default= ['host'=>'localhost', 'username'=>'root', 'password'=>'root'];
$custom = ['username'=>'admin', 'password'=>'123456'];
$res = array_merge($default, $custom);
printf('<pre>%s</pre>', print_r($res,true));
echo '<hr>';
// 3. array_chunk`: 将一个数组分割成多个子数组,在海量数据查询时非常实用
// 注意, 这是数据分块, 不是分页, 也不能代替分页查询
$arr = range(1,20);
$res = array_chunk($arr, 6);
printf('<pre>%s</pre>', print_r($res,true));
```
---
### 2.9 自动生成
| 序号 | 函数 | 描述 |
| ---- | ----------------- | ---------------------------- |
| 1 | `array_fill` | 用给定的值填充数组 |
| 2 | `array_fill_keys` | 使用指定的键和值填充数组 |
| 3 | `array_pad` | 以指定长度将一个值填充进数组 |
示例:
```php
// 自动生成
// 1.array_fill: 用给定的值填充数组
$res = array_fill(2, 5, 'demo');
printf('<pre>%s</pre>', print_r($res, true));
echo '<hr>';
// 2. array_fill_keys: 使用指定的键和值填充数组,注意键名各不相同, 但值是一样的
// 先创建一个键名数组
$keys = ['id', 'name', 'age', 'email'];
$res = array_fill_keys($keys, 'demo');
printf('<pre>%s</pre>', print_r($res, true));
echo '<hr>';
// 3. array_pad`: 以指定长度将一个值填充进数组
$arr = ['apple', 'dell', 'thinkpad'];
$res = array_pad($arr, 6, 'computer');
printf('<pre>%s</pre>', print_r($res, true));
```
---
### 2.10 类型转换
| 序号 | 函数 | 描述 |
| ---- | --------- | ---------------------------------------- |
| 1 | `list` | 将数组中的值赋予一组变量(类似解构赋值) |
| 2 | `implode` | 将数组元素按指定字符拼装成字符串 |
| 3 | `explode` | 将字符串分割为数组 |
| 4 | `extract` | 将关联数组拆分成变量名值对 |
| 5 | `compact` | 将一组变量名值对拼装成一个关联数组键值对 |
示例:
```php
// 类型转换
// 1.list: 将数组中的值赋予一组变量(类似解构赋值)
// 索引数组转变量
list($id, $name) = [10, 'admin'];
printf('$id = %s, $name = %s <br>', $id, $name);
// php7+, 支持关联数组元素转变量
list('id'=>$id, 'name'=>$name) = ['id'=>20, 'name'=>'peter'];
printf('$id = %s, $name = %s <br>', $id, $name);
// 取一部分值
list(,,$email) = [10, 'admin', 'admin@php.cn'];
echo $email . '<br>';
echo '<hr>';
// 2. implode: 将数组元素按指定字符拼装成字符串
$arr = ['huawei', 'xiaome', 'apple', 'oppo', 'vivo'];
echo implode(', ', $arr);
echo '<hr>';
// 3. explode`: 将字符串分割为数组
$str = 'blue, green, yellow, red, coral';
// 注意, 逗号后面要有一个空格, 这样才能与原始字符串对应上
$res = explode(', ', $str);
printf('<pre>%s</pre>', print_r($res, true));
echo '<hr>';
// 4. extract: 将关联数组拆分成变量名值对
// 以pdo连接参数为例进行演示
$config = ['type'=>'mysql','host'=>'localhost', 'dbname'=>'phpedu', 'charset'=>'utf8'];
extract($config);
// 拼装dsn字符串
$dsn = sprintf('%s:host=%s; dbname=%s; charset=%s',$type,$host, $dbname, $charset);
echo $dsn , '<br>';
// 连接数据库,先创建数据库phpedu,防止连接失败, 用户名密码采用默认值:root
$pdo = new PDO($dsn, 'root', 'root');
var_dump($pdo);
echo '<hr>';
// 5. compact: 将一组变量名值对拼装成一个关联数组键值对
$id = 99;
$name = 'Peter Zhu';
$job = 'Lecture';
// 传统方式, 手工创建关联数组,将变量值做为元素值填充
$res = ['id'=>$id, 'name'=>$name, 'job'=>$job];
printf('<pre>%s</pre>', print_r($res, true));
// 使用compace()可一步到位
$res = compact('id', 'name', 'job');
printf('<pre>%s</pre>', print_r($res, true));
```
---
### 2.11 回调处理
| 序号 | 函数 | 描述 |
| ---- | -------------- | ---------------------------------------------- |
| 1 | `array_filter` | 用回调函数过滤数组中的单元 |
| 2 | `array_map` | 为数组的每个元素应用回调函数 |
| 3 | `array_reduce` | 用回调函数迭代地将数组简化为单一的值 |
| 4 | `array_walk` | 使用用户自定义函数对数组中的每个元素做回调处理 |
示例:
```php
// 数组元素的回调处理
// 1. array_filter: 用回调函数过滤数组中的单元,返回回调执行为true的元素组成的新数组
$arr = [150,'php', true,[4,5,6], (new class {}), [], null, false,'', 0, '0'];
// 利用回调只返回运算结果为true元素特征, 可以过滤掉数组中的可转为false的值
// 自动转换为false的值包括但不限于: null, false, 空数组, 空字符串, 0, '0'
// 提示: 空对象不能转为false, 空数组可以
// 省略回调参数, 则自动过滤掉数组中可转为false的值,实现过滤空值的效果
$res = array_filter($arr);
printf('<pre>%s</pre>', print_r($res,true));
// 自定义回调函数参数, 返回运算结果为true的值组成的数组
$res = array_filter($arr, function($value) {
// 只返回标量类型的元素: interger, float, string, boolean
// 所以, 被过滤掉的元素类型是array, object, 包括空数组,空对象
return is_scalar($value);
});
printf('<pre>%s</pre>', print_r($res,true));
echo '<hr>';
// 2 array_map: 为数组的每个元素应用回调函数,返回回调处理后的所有元素组成的 "新数组"
$arr = ['php',[3,4,5], (new class {public $name='电脑';public $price=8888;}), 15, 20];
$res = array_map(function ($item){
switch (gettype($item)) {
case 'object':
$item = get_object_vars($item);
case 'array':
$item = implode(', ', $item);
}
return $item;
}, $arr);
printf('<pre>%s</pre>', print_r($res,true));
// 同时处理多个数组,例如将一个键名与值的二个数组进行组装
$keys = ['host', 'username', 'password'];
$values = ['localhost', 'root', '123456'];
// 当然,使用前面学过的: array_combine()可以轻松实现
$res = array_combine($keys, $values);
printf('<pre>%s</pre>', print_r($res,true));
// 其实,使用array_map()也可以实现同样功能
$res = array_map(function ($value1, $value2) {
return [$value1 => $value2];
}, $keys, $values);
printf('<pre>%s</pre>', print_r($res,true));
// 不过, 返回的是一个二维数组, 如果要获取用户名,需要这样做,比较麻烦
echo '用户名: ' . $res[1]['username'];
// 下面我们用数组迭代函数实现同样功能来拉平这个数组,模拟array_combine()函数的功能
echo '<hr>';
// 自定义数组迭代处理函数: my_array_reduce($array, $callback, $init=null)
// $callback回调中的二个参数
// $prev: 上一次迭代处理的结果,如果是第一次迭代,它的值是第三个参数$init提供的初始值
// $current: 本次需要迭代处理的值
// $init: 可选, 在迭代处理前,或者处理结束后,数组为空时的最后一个结果
function my_array_reduce($array, $callback, $init=null)
{
// $prev的初始值
$prev = $init;
foreach($array as $current){
// 为数组中每一个元素调用迭代回调函数进行处理
$prev = $callback($prev, $current);
}
// 将最终结果通过前一次迭代变量返回
return $prev;
}
$result = my_array_reduce($res, function ($prev, $current) {
// 获取当前元素的键值
$key = key($current);
$value = current($current);
// 将键值对拼装成一个元素, 利用$prev,将结果返回
$prev[$key] = $value;
return $prev;
});
printf('自定义数组迭代函数返回值:<pre>%s</pre>', print_r($result,true));
echo '<hr>';
// 幸运的是, 我们不用自定义数组迭代处理函数, 系统已经为我们预置了
// 3. array_reduce($arr, $callback($prev, $current), $init): 用回调函数迭代地将数组简化为单一的值
// $res: 它是前面array_map()的返回值,是一个二维数组
$res = array_reduce($res, function ($prev, $current) {
// 获取当前元素的键值
$key = key($current);
$value = current($current);
// 将键值对拼装成一个元素, 利用$prev,将结果返回
$prev[$key] = $value;
return $prev;
});
printf('内置数组迭代函数返回值:<pre>%s</pre>', print_r($res,true));
echo '<hr>';
// 4. array_walk: 使用用户自定义函数对数组中的每个元素做回调处理, 返回布尔值
// array_walk($arr, callback($value, $key), $userdata)
// 回调的第一个参数$value,如果设置为引用(&$value),则所做的修改直接作用到原始数组本身
// 如果提供了第三个参数$userdata,则做为回调的第三个参数传入
// 这个函数不受数组内部指针约束, 不论当前指针在哪,都会遍历整个数组
// 该函数主要用于在遍历数组的时候进行一些自定义操作, 例如元素的格式化等, 所以常在回调中使用输出语句
// 例如,将数组元素的值,描红输出
$res = ['id'=>123, 'name'=>'peter', 'email'=>'peter@php.cn'];
array_walk($res, function ($value, $key, $color){
printf('[ %s ] => <span style="color:%s">%s</span><br>', $key, $color, $value);
}, 'red');
```