PHP中的数组函数学习(二)

继续我们关于数组函数的学习。上篇文章中说过,数组在 PHP 中是一等一的公民,也是我们最常用的一种数据结构。对于大家来说,或许你还没有正式的学习过数据结构,但你一定听说过栈、队列这些名词,那么我们就先以数组中的队列、栈式操作为入口开始今天的内容。

队列、栈式操作

在数据结构的系列文章中,我们就说过数组可以代表顺序存储结构形式,所以,PHP 中也提供了非常方便的对于数组的一些算法操作,比如栈的操作。

$arr = ['a', 'b'];

print_r(array_push($arr, 'c')); // 3
print_r($arr);
// Array
// (
//     [0] => a
//     [1] => b
//     [2] => c
// )

echo array_pop($arr), PHP_EOL; // c
print_r($arr);
// Array
// (
//     [0] => a
//     [1] => b
// )

通过 array_push() ,我们可以将一个元素压到数组的最后一位,就像压栈操作一样。而 array_pop() 则是获取并删除最后一个元素,这就像是我们的出栈操作一样。是不是非常地方便。当然,队列操作也有类似的功能,先来看看从数组的头部进行添加和删除元素的操作。

echo array_unshift($arr, 'c'), PHP_EOL; // 3
print_r($arr);
// Array
// (
//     [0] => c
//     [1] => a
//     [2] => b
// )

echo array_shift($arr), PHP_EOL; // c
print_r($arr);
// Array
// (
//     [0] => a
//     [1] => b
// )

array_unshift() 用于将一个元素放到数组的最前面,对于数字下标的数组来说,其它原来存在的元素会依次向后移。array_shift() 则是从数组的顶部取出并删除一个元素。对于完善的队列操作来说,我们可以用 array_push() 入队,然后使用 array_shift() 出队,这就是一个非常典型的队列操作了。

array_push() 和 array_unshift() 函数也可以一次加入多个元素到数组中。

array_push($arr, 'c', 'd', 'e');
print_r($arr);
// Array
// (
//     [0] => a
//     [1] => b
//     [2] => c
//     [3] => d
//     [4] => e
// )

当然,官方文档中其实不建议使用这个 array_push() ,因为我们可以直接使用 $arr[] = xxx; 这种形式来将数据添加到数组的末尾,同时,这样的语法还不需要经过函数调用,性能更好一些。当然,具体的选择还是要看我们的业务情况,这里也只是一个建议。

随机取出数组中的键

这里的随机取出来的是数组的键,也就是 key 的值,而不是数组的值。

echo array_rand(['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c']); // 6

print_r(array_rand(['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'], 3));
// Array
// (
//     [0] => 0
//     [1] => baz
//     [2] => 6
// )

虽然 array_rand() 的名称会让人感觉是随机取出一个数组的值出来,但它确实只是随机的返回数组的键。如果给第二个参数赋值大于 1 的数字的话,那么它将返回的是一个数组,里面包含多个随机取得的键。

获取数组所有值的乘积

这个其实是一个聚合操作函数。就像 MySQL 中的 sum() 类似的功能,不过它可以获取数组中所有元素值的乘积。

echo array_product([2,3,4]), PHP_EOL; // 24

echo array_product([2,3,'c']), PHP_EOL; // 0

echo array_product([]), PHP_EOL; // 1

需要注意的是,如果数组中包含字符串的话,那么返回的结果会是 0 。而如果是一个空数组的话,那么返回的是 1 。这一点是比较特殊的地方,照理说,如果有字符串存在它的计算结果应该和空数组是类似的才对,而且在老版本的 PHP 中确实空数组是返回的 0 ,但现在空数组返回的结果是 1 ,这一点大家一定要记清楚,免得在使用的时候因为这个问题而产生不可预知的 BUG 。

填补数组

这里的填补数组和我们上篇文章中的填充数组是有区别的。

print_r(array_pad(['a', 'b'], 5, 'cc'));
// Array
// (
//     [0] => a
//     [1] => b
//     [2] => cc
//     [3] => cc
//     [4] => cc
// )

print_r(array_pad(['a', 'b'], -5, 'cc'));
// Array
// (
//     [0] => cc
//     [1] => cc
//     [2] => cc
//     [3] => a
//     [4] => b
// )

print_r(array_pad(['a', 'b'], 2, 'cc'));
// Array
// (
//     [0] => a
//     [1] => b
// )

array_fill() 一般是针对空的数组填充,用于数组的新建,而 array_pad() 一般是针对已经存在的数组进行填补。在上面的例子中,如果第二个参数是负数的话,就是从前面开始填补。如果这个参数和数组的数量一致或者小于数组的长度的话,那么就不会填补数据。

遍历数组

遍历数组的这个 array_map() 函数其实也是比较常见的一个函数。

// 遍历数组
array_map(function($v){
    
    echo "元素值为:{$v} .", PHP_EOL;

}, ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c']);
// 元素值为:a .
// 元素值为:b .
// 元素值为:1 .
// 元素值为:2 .
// 元素值为:c .

其实它就是在回调函数中对每个元素进行遍历,我们可以在这个回调函数中对数组的所有数据进行一些操作。这个就不多说了,不过它还有更好玩的用法,比如我们可以同时操作多个数组。

array_map(function($v1, $v2){
    
    echo "元素值为:{$v1}, {$v2} .", PHP_EOL;

}, ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'], ['d', 'e']);
// 元素值为:a, d .
// 元素值为:b, e .
// 元素值为:1,  .
// 元素值为:2,  .
// 元素值为:c,  .

它们的遍历操作是同时进行的,每多一个数组,这个回调函数就多一个参数,参数的内容就是数组的值。这个使用方式确实是平常没有注意到的,也是这次学习中发现的非常好玩的一点。

键操作

在数组中,键和值都是非常重要的数据内容。PHP 中为我们提供了丰富的专门针对键的操作函数,比如我们想获取一个数组中所有键的信息,就可以使用下面这个函数。

print_r(array_keys(['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c']));
// Array
// (
//     [0] => 0
//     [1] => 1
//     [2] => foo
//     [3] => baz
//     [4] => 6
// )

array_keys() 返回的是一个包含指定数组中全部键信息的数组。它的使用比较简单,当然,我们也可以直接获取数组中指定位置的键信息。

echo array_key_first(['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c']), PHP_EOL; // 0
echo array_key_last(['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c']), PHP_EOL; // 6

除了获取键的信息外,我们还可以判断数组中是否存在某个键,这个函数想必也是大家比较常见的。

var_dump(array_key_exists('foo', ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'])); // bool(true)
var_dump(array_key_exists('foo1', ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'])); // bool(false)
var_dump(array_key_exists(6, ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'])); // bool(true)

var_dump(key_exists('foo', ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'])); // bool(true)

注意,key_exists() 和 array_key_exists() 是一样的,key_exists() 是 array_key_exists() 的一个别名函数。

echo key(['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c']), PHP_EOL; // 0

key() 函数用于获取当前数组中当前游标位置的键值。这个是类似于游标操作的一个函数,所以我们可以配合其它游标操作函数来进行数组的遍历。

$arr = ['a', 'b', 'foo' => 1, 'baz' => 2, 6 => 'c'];
while ($v = current($arr)) {
    echo key($arr), PHP_EOL;
    next($arr);
}
// 0
// 1
// foo
// baz
// 6

关于这种类似游标的操作,之前在关于让类可遍历的文章中也讲到过,其实 foreach() 的内部实现也是依赖于类似的这种游标循环操作。当然,在我们的现代化开发中,除了一些特殊情况之外,当然还是使用 foreach() 来得更加方便直观。

交集操作

最后就是我们的交集操作。上篇文章中我们已经学过了数组的差集的操作,今天的交集操作其实也是类似的,包括值的比对、键的比对、回调函数方式的函数这些,所以就不多做解释了,只是贴出代码大家自已运行测试一下。

print_r(array_intersect(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x']));
// Array
// (
//     [1] => 1
//     [2] => b
//     [3] => c
//     [4] => x
// )

print_r(array_intersect_assoc(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x']));
// Array
// (
//     [4] => x
// )

print_r(array_intersect_key(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', 'x'], ['a', 'c', 'b', '1', 'x']));
// Array
// (
//     [0] => a
//     [1] => 1
//     [2] => b
//     [3] => c
// )

print_r(array_intersect_key(['a' => 1, 'b' => 2], ['b' => 3, 'd' => 4]));
// Array
// (
//     [b] => 2
// )

就和差集操作的函数名都带一个 diff 一样,交集操作的函数名都带一个 intersect 。大家只要记住这一块就可以了,其它的函数名称都和差集操作是类似的。

回调函数交集操作

同样的,带回调的操作函数也是类似的。

print_r(array_intersect_uassoc(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x'], function ($a, $b) {
    return $a <=> $b;
}));
// Array
// (
//     [4] => x
// )

print_r(array_intersect_ukey(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x'], function ($a, $b) {
    return $a <=> $b;
}));
// Array
// (
//     [0] => a
//     [1] => 1
//     [2] => b
//     [3] => c
//     [4] => x
// )

print_r(array_uintersect(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x'], function ($a, $b) {
    return $a <=> $b;
}));
// Array
// (
//     [2] => b
//     [3] => c
//     [4] => x
// )

print_r(array_uintersect(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x'], function ($a, $b) {
    return $a <=> $b;
}));
// Array
// (
//     [2] => b
//     [3] => c
//     [4] => x
// )

print_r(array_uintersect_assoc(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x'], function ($a, $b) {
    return $a <=> $b;
}));
// Array
// (
//     [4] => x
// )

print_r(array_uintersect_uassoc(['a', 1, 'b', 'c', 'x'], ['b', 'c', 'd', '1', 'x'], ['a', 'c', 'b', '1', 'x'], function ($a, $b) {
    return $a <=> $b;
}, function ($a, $b) {
    return $a <=> $b;
}));
// Array
// (
//     [4] => x
// )

总结

今天学习的内容依然只是数组相关操作函数中的一小部分。我们了解到了一些数据结构相关操作函数的使用,也看到了交集操作的功能函数的应用。这些功能其实在我们日常的业务开发中还是挺常用的,比如在我做的统计系统中就经常会有交、差相关的操作,灵活使用这些函数能够大大方便我们的业务开发,大家可以多多深入研究好好掌握哦!

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/04/source/1.PHP%E4%B8%AD%E7%9A%84%E6%95%B0%E7%BB%84%E5%87%BD%E6%95%B0%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89.php

参考文档:

https://www.php.net/manual/zh/ref.array.php