PHP数组最佳实践

在这篇教程里,我将使用实际的例子来介绍一些常用的PHP数组及其最佳实践。每个PHP开发者都必须知道如何使用他们,并且组合使用这些数组函数写出可读性好、简洁的代码。

基本

让我们从最基本的处理数组键值的函数开始吧。首先是 array_combine(),它使用一个数组做键,一个数组做值,创建一个新的数组:

$keys = ['sky', 'grass', 'orange'];
$values = ['blue', 'green', 'orange'];
 
$array = array_combine($keys, $values);
print_r($array);
 
// Array
// (
//     [sky] => blue
//     [grass] => green
//     [orange] => orange
// )

你应该知道,array_values() 函数返回原数组的值组成的数组,array_keys() 返回原数组的键组成的数组,array_filp() 交换原数组的键和值:

print_r(array_keys($array)); // ['sky', 'grass', 'orange']
print_r(array_values($array)); // ['blue', 'green', 'orange']
print_r(array_flip($array));
 
// Array
// (
//     [blue] => sky
//     [green] => grass
//     [orange] => orange
// )

写更简洁的代码

函数 list(),实际上是一个语言结构,其目的就是更简洁地赋值变量。下面是一个基本的例子:

// define array
$array = ['a', 'b', 'c'];
 
// without list()
$a = $array[0];
$b = $array[1];
$c = $array[2];
 
// with list()
list($a, $b, $c) = $array;

这个语言结构与 preg_list() 和 explode() 配合使用更好,同时,你可以省略某些参数(如果你不想多定义一个参数的话):

$string = 'hello|wild|world';
list($hello, , $world) = explode('|', $string);
echo("$hello, $world"); // hello, world

list() 配合 foreach 使用也会取得更好的效果:

$arrays = [[1, 2], [3, 4], [5, 6]];
 
foreach ($arrays as list($a, $b)) {
    $c = $a + $b;
    echo($c . ', '); // 3, 7, 11, 
}

extract() 函数可以把一个关联数组赋值给变量。对于一个数组的每个元素,创建一个变量,元素的键做变量名,元素的值做变量值:

$array = [
    'clothes' => 't-shirt',
    'size'    => 'medium',
    'color'   => 'blue',
];
 
extract($array);
 
echo("$clothes $size $color"); // t-shirt medium blue

与 extract() 作用相反的函数是 compact() 函数,它使用变量来创建一个关联数组:

$clothes = 't-shirt';
$size = 'medium';
$color = 'blue';
 
$array = compact('clothes', 'size', 'color');
print_r($array);
 
// Array
// (
//     [clothes] => t-shirt
//     [size] => medium
//     [color] => blue
// )

过滤函数

有一个极好的数组过滤函数,叫做 array_filter()。第一个参数是待过滤的数组,第二个参数是一个匿名函数,如果你想要让元素留在数组中,就让该回调函数返回true,否则返回dalse:

$numbers = [20, -3, 50, -99, 55];
 
$positive = array_filter($numbers, function($number) {
    return $number > 0;
});
 
print_r($positive); // [0 => 20, 2 => 50, 4 => 55]

array_filter 不仅可以通过数组值来过滤,也可以使用 ARRAY_FILTER_USE_KEY 和 ARRAY_FILTER_USE_BOTH 作为第三个参数来决定传递键或键值到回调函数中。

你也可以调用 array_filter() 不传回调函数,这样会过滤所有的空值:

$numbers = [-1, 0, 1];
 
$not_empty = array_filter($numbers);
 
print_r($not_empty); // [0 => -1, 2 => 1]

你可以使用 array_unique() 函数从数组中获取唯一值,要注意这个函数会保留一个唯一元素的键:

$array = [1, 1, 1, 1, 2, 2, 2, 3, 4, 5, 5];
$uniques = array_unique($array);
 
print_r($uniques);
 
// Array
// (
//     [0] => 1
//     [4] => 2
//     [7] => 3
//     [8] => 4
//     [9] => 5
// )

使用 array_column(),可以从多维数组数组中获得一列值组成的数组,类似于从SQL数据库中获取值一样:

$array = [
    ['id' => 1, 'title' => 'tree'],
    ['id' => 2, 'title' => 'sun'],
    ['id' => 3, 'title' => 'cloud'],
];
 
$ids = array_column($array, 'id');
 
print_r($ids); // [1, 2, 3]

从PHP7开始,array_column() 变得更加强大了,因为它现在支持对象组成的数组了,所以用它来处理模型的数组变得更容易:

$cinemas = Cinema::find()->all();
$cinema_ids = array_column($cinemas, 'id'); // php7 forever!

遍历数组

使用 array_map() 可以将一个回调函数应用到数组的每个元素上面。你可以传递一个函数名或一个匿名函数,基于给定的数组获取新的数组:

$cities = ['Berlin', 'KYIV', 'Amsterdam', 'Riga'];
$aliases = array_map('strtolower', $cities);
 
print_r($aliases); // ['berlin', 'kyiv, 'amsterdam', 'riga']
 
$numbers = [1, -2, 3, -4, 5];
$squares = array_map(function($number) {
    return $number ** 2;
}, $numbers);
 
print_r($squares);  // [1, 4, 9, 16, 25]

有一个传言说没有办法同时传递键和值给回调函数,但是可以这样做到:

$model = ['id' => 7, 'name'=>'James'];
 
$callback = function($key, $value) {
    return "$key is $value";
};
 
$res = array_map($callback, array_keys($model), $model);
print_r($res);
 
// Array
// (
//     [0] => id is 7
//     [1] => name is James
// )

但是这样看起太丑陋了,更好的方式是使用 array_walk() 替代。这个函数类似 array_map(),但工作原理不同。首先,数组是通过引用传递的,所以 array_walk() 不会创建新数组,而是修改原数组。

$fruits = [
    'banana' => 'yellow',
    'apple' => 'green',
    'orange' => 'orange',
];
 
array_walk($fruits, function(&$value, $key) {
    $value = "$key is $value";
});
 
print_r($fruits);
 
// Array
// (
//     [banana] => banana is yellow
//     [apple] => apple is green
//     [orange] => orange is orange
// )

合并数组

合并多个数组的最好方式是使用 array_merge() 函数。数组元素会被合并到一起,对于具有相同键的值,前面的元素会被后面值重写:

$array1 = ['a' => 'a', 'b' => 'b', 'c' => 'c'];
$array2 = ['a' => 'A', 'b' => 'B', 'D' => 'D'];
 
$merge = array_merge($array1, $array2);
print_r($merge);
// Array
// (
//     [a] => A
//     [b] => B
//     [c] => c
//     [D] => D
// )

如果想从数组中移除另一个数组的值,可使用 array_diff() 函数。如果想从多个数组中获取它们都有的值,可使用 array_intersect()。下面的例子:

$array1 = [1, 2, 3, 4];
$array2 =       [3, 4, 5, 6];
 
$diff = array_diff($array1, $array2);
print_r($diff); // [0 => 1, 1 => 2]
 
$intersect = array_intersect($array1, $array2);
print_r($intersect);  // [2 => 3, 3 => 4]

使用数组做数学计算

使用 array_sum() 获取数组值的和,array_product() 获取数组值的乘积,或使用 array_reduce() 创建你自己的公式:

$numbers = [1, 2, 3, 4, 5];
 
echo(array_sum($numbers)); // 15
echo(array_product($numbers)); // 120
 
echo(array_reduce($numbers, function($carry, $item) {
    return $carry ? $carry / $item : 1;
})); // 0.0083 = 1/2/3/4/5

使用 array_count_values() 计算数组中值出现的次数,每个唯一的键作为数组的键,键值出现的次数作为值:

$things = ['apple', 'apple', 'banana', 'tree', 'tree', 'tree'];
$values = array_count_values($things);
 
print_r($values);
 
// Array
// (
//     [apple] => 2
//     [banana] => 1
//     [tree] => 3
// )

生成数组

可以使用 array_fill() 从给定的大小和值来生成数组:

$bind = array_fill(0, 5, '?');
print_r($bind); // ['?', '?', '?', '?', '?']

可以使用 range() 生成顺序的值:

$letters = range('a', 'z');
print_r($letters); // ['a', 'b', ..., 'z']
 
$hours = range(0, 23);
print_r($hours); // [0, 1, 2, ..., 23]

可以使用 array_slice() 获取数组的一部分:

$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$top = array_slice($numbers, 0, 3);
print_r($top); // [1, 2, 3]

排序数组

最好要记住的是,所有的数组排序函数使用的都是数组的引用,成功返回true,失败返回false。首先是一个基本的排序函数 sort(),它按照值的升序顺序排序数组元素,不会保留键值。其他的排序函数都是在 sort() 函数名前加上不同的字母前缀来表示:

  • a,排序并保留键
  • k,按照键来排序
  • r,按照相反/降序的顺序排序
  • u,按照用户定义的函数来排序
    下表是这些字母的组合:

| |a| k |r |u |
| ———— | ———— | ———— | ———— |
| a| asort| | arsort | uasort |
| k| | ksort | krsort | |
| r| arsort | krsort | rsort | |
| u| uasort | | | usort |

当你开始组合使用数组函数时,真正的魔法才开始展现。下面是一个例子:组合 array_slice() 和 array_map() 函数,仅仅使用两行代码就完成了trim和空值的移除:

$values = ['say  ', '  bye', ' ', ' to', ' spaces ', '   '];
 
$words = array_filter(array_map('trim', $values));
print_r($words); // ['say', 'bye', 'to', 'spaces']

组合使用 array_combine() 和 array_column() 函数,可以从模型数组中获取id到title的映射:

$models = [$model1, $model2, $model3];
 
$id_to_title = array_combine(
    array_column($models, 'id'),
    array_column($models, 'title')
);

组合使用 array_count_value(),arsort() 和 array_slice() 函数,获取前三个出现次数最多的值:

$letters = ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'd', 'd', 'd', 'd', 'd'];
 
$values = array_count_values($letters); // get key to count array
arsort($values); // sort descending preserving key
$top = array_slice($values, 0, 3); // get top 3
 
print_r($top);
// Array
// (
//     [d] => 5
//     [a] => 4
//     [b] => 2
// )

使用 array_sum() 和 array_map() 很容易计算某行的和:

$order = [
    ['product_id' => 1, 'price' => 99, 'count' => 1],
    ['product_id' => 2, 'price' => 50, 'count' => 2],
    ['product_id' => 2, 'price' => 17, 'count' => 3],
];
 
$sum = array_sum(array_map(function($product_row) {
    return $product_row['price'] * $product_row['count'];
}, $order));
 
print_r($sum); // 250

总结

如上所述,主要的数组函数可使代码更具可读性,更简洁。当然,PHP还有很多其他的数组函数,就算是上述的数组函数给定不同的参数的话也会有不同的用法,但是我认为这篇教程中的内容已经覆盖了每个PHP开发者应该要知道的东西。

注:这篇教程是我翻译自一篇英文文章,原文链接贴在下面了:
https://code.tutsplus.com/tutorials/working-with-php-arrays-in-the-right-way--cms-28606