Arrays

如果你对其他编程语言或数据结构有一定的了解,你可能会知道两种非常常见和有用的数据结构:listsmapslist 是元素的有序集合,而 map 则是由键标识的元素集合。让我们来看一个例子:

List: ["Harry", "Ron", "Hermione"]

Map: {
    "name": "James Potter",
    "status": "dead"
}

第一个元素是一个姓名列表,包含三个值:Harry, Ron,Hermione。第二个元素是一个地图,定义了两个值: James Potter 和 dead。这两个值分别用一个键来标识:name 和 status。

在 PHP 中,我们没有 lists 和 maps,我们有数组。数组是一种同时实现 list 和 map 的数据结构。

初始化数组

在初始化数组时,你有不同的选择。你可以初始化一个空数组,也可以初始化一个包含数据的数组。用数组写入相同的数据也有不同的方法。让我们来看几个例子:

<?php

$empty1 = [];
$empty2 = array();
$names1 = ['Harry', 'Ron', 'Hermione'];
$names2 = array('Harry', 'Ron', 'Hermione');
$status1 = [
    'name' => 'James Potter',
    'status' => 'dead'
];
$status2 = array(
    'name' => 'James Potter',
    'status' => 'dead'
);

在前面的示例中,我们定义了上一节中的 list 和 map。$names1$names2 是完全相同的数组,只是使用了不同的符号。$status1$status2 也是同样的情况。最后,$empty1$empty2 是创建空数组的两种方法。

稍后你会看到列表的处理方式与地图类似。在内部,数组 $names1 是一个映射,它的键是有序的数字。在这种情况下,$names1 的另一种初始化方式也可以产生相同的数组,如下所示:

$names1 = [
    0 => 'Harry',
    1 => 'Ron',
    2 => 'Hermione'
];

数组的键可以是任何字母数字值,如字符串或数字。数组的值可以是任何东西:字符串、数字、布尔值、其他数组等。数组的值可以是任何东西:字符串、数字、布尔值、其他数组等等:

<?php
$books = [
    '1984' => [
        'author' => 'George Orwell',
        'finished' => true,
        'rate' => 9.5
    ],
    'Romeo and Juliet' => [
        'author' => 'William Shakespeare',
        'finished' => false
    ]
];

该数组是一个包含两个数组——maps 的列表。每个映射都包含不同的值,例如字符串、双精度值和布尔值。

填充数组

数组不是不可变的,也就是说,数组在初始化后会发生变化。你可以把数组当作映射或列表来更改其内容。将数组作为映射处理意味着要指定要覆盖的键,而将数组作为列表处理意味着要在数组末尾添加另一个元素:

<?php
$names = ['Harry', 'Ron', 'Hermione'];
$status = [
    'name' => 'James Potter',
    'status' => 'dead'
];
$names[] = 'Neville';
$status['age'] = 32;
print_r($names, $status);

在前面的示例中,第一行突出显示的内容是在名字列表中添加 Neville 这个名字,因此列表看起来就像 ['Harry', 'Ron', 'Hermione', 'Neville']。第二个改动实际上是在数组中添加了一个新的键值。您可以使用 print_r 函数从浏览器中查看结果。它的功能与 var_dump 类似,只是不显示每个值的类型和大小。

浏览器中的 print_r 和 var_dump

在打印数组内容时,每行显示一个键值是很有用的,但如果检查浏览器,就会发现它在一行中显示了整个数组。这是因为浏览器试图显示的是 HTML,它忽略了新行或空格。要查看 PHP 希望看到的数组内容,请查看页面的源代码—​右击页面即可看到该选项。

如果需要从数组中删除一个元素,而不是添加或更新一个元素,可以使用 unset 函数:

<?php
$status = [
    'name' => 'James Potter',
    'status' => 'dead'
];
unset($status['status']);
print_r ($status);

新的 $status 数组仅包含键 name

访问数组

访问数组就像指定更新时的键一样简单。为此,你需要了解列表的工作原理。我们已经知道,列表在内部被视为一个按顺序排列数字键的映射。第一个键总是 0;因此,一个有 n 个元素的数组将有从 0 到 n-1 的键。

你可以在给定的数组中添加任何键,即使它以前是由数字条目组成的。问题出现在添加数字键时,之后你试图向数组追加一个元素。你认为会发生什么情况?

<?php
$names = ['Harry', 'Ron', 'Hermione'];
$names['badguy'] = 'Voldemort';
$names[8] = 'Snape';
$names[] = 'McGonagall';
print_r($names);

最后一段代码的结果如下:

Array
(
    [0] => Harry
    [1] => Ron
    [2] => Hermione
    [badguy] => Voldemort
    [8] => Snape
    [9] => McGonagall
)

在尝试追加数值时,PHP 会将其插入最后一个数字键(本例中为 8)之后。

你可能自己已经明白了,但你总是可以通过指定数组的键来打印数组的任何部分:

<?php
$names = ['Harry', 'Ron', 'Hermione'];
print_r($names[1]); // prints 'Ron'

最后,尝试访问数组中不存在的键时,PHP 会返回一个空值并抛出一个通知,因为 PHP 会识别出你的代码中做错了什么。

<?php
$names = ['Harry', 'Ron', 'Hermione'];
var_dump($names[4]); // null and a PHP notice

空函数和 isset 函数

有两个有用的函数可以查询数组的内容。如果想知道数组中是否包含任何元素,可以使用 empty 函数询问数组是否为空。该函数实际上也适用于字符串,空字符串就是没有字符('')的字符串。isset 函数接收一个数组位置,并根据该位置是否存在返回 truefalse

<?php
$string = '';
$array = [];
$names = ['Harry', 'Ron', 'Hermione'];
var_dump(empty($string)); // true
var_dump(empty($array)); // true
var_dump(empty($names)); // false
var_dump(isset($names[2])); // true
var_dump(isset($names[3])); // false

在前面的示例中,我们可以看到一个没有元素的数组或一个没有字符的字符串在被问及是否为空时将返回 true,否则返回 false。当我们使用 isset($names[2]) 检查数组的位置 2 是否存在时,我们得到的结果是 true,因为该键有一个值:Hermione。最后,isset($names[3]) 的结果是 false,因为数组中不存在键 3

搜索数组中的元素

使用数组最多的函数之一可能就是 in_array。该函数接收两个值,一个是要搜索的值,另一个是数组。如果值在数组中,函数返回 true,否则返回 false。这个函数非常有用,因为很多时候你想从列表或地图中知道的是它是否包含某个元素,而不是知道它是否包含或它的位置。

有时更有用的是 array_search。该函数的工作方式与之相同,但它返回的不是布尔值,而是找到值的键,否则返回 false。让我们来看看这两个函数:

<?php
$names = ['Harry', 'Ron', 'Hermione'];
$containsHermione = in_array('Hermione', $names);
var_dump($containsHermione); // true
$containsSnape = in_array('Snape', $names);
var_dump($containsSnape); // false
$wheresRon = array_search('Ron', $names);
var_dump($wheresRon); // 1
$wheresVoldemort = array_search('Voldemort', $names);
var_dump($wheresVoldemort); // false

对数组进行排序

数组可以用不同的方式排序,因此你需要的排序很可能与当前的不同。默认情况下,数组是按元素加入的顺序排序的,但你也可以按键或按值对数组进行升序或降序排序。此外,按值排序时,可以选择保留数组的键值,或以列表形式生成新的键值。

官方文档网站 http://php.net/manual/en/array.sorting.php 上有这些功能的完整列表,但我们将在这里展示最重要的功能:

Name Sorts by Maintains key association Order of sort

sort

Value

No

Low to high

rsort

Value

No

High to low

asort

Value

Yes

Low to high

arsort

Value

Yes

High to low

ksort

Key

Yes

Low to high

krsort

Key

Yes

High to low

这些函数始终只有一个参数,即数组,而且不返回任何内容。相反,它们会直接对我们传递给它们的数组进行排序。让我们来看看其中的一些:

<?php
$properties = [
    'firstname' => 'Tom',
    'surname' => 'Riddle',
    'house' => 'Slytherin'
];
$properties1 = $properties2 = $properties3 = $properties;
sort($properties1);
var_dump($properties1);
asort($properties3);
var_dump($properties3);
ksort($properties2);
var_dump($properties2);

好了,上一个示例中有很多事情要做。首先,我们用一些键值初始化了一个数组,并将其赋值给 $properties。然后,我们创建了三个变量,它们是原始数组的副本—​语法应该很直观。为什么要这么做呢?因为如果我们对原始数组进行排序,就不会再有原始内容了。在这个特定的例子中,这并不是我们想要的,因为我们想看看不同的排序功能是如何影响同一个数组的。最后,我们执行三种不同的排序,并打印出每种结果。浏览器应该会显示如下内容:

array(3) {
    [0]=>
    string(6) "Riddle"
    [1]=>
    string(9) "Slytherin"
    [2]=>
    string(3) "Tom"
}
array(3) {
    ["surname"]=>
    string(6) "Riddle"
    ["house"]=>
    string(9) "Slytherin"
    ["firstname"]=>
    string(3) "Tom"
}
array(3) {
    ["firstname"]=>
    string(3) "Tom"
    ["house"]=>
    string(9) "Slytherin"
    ["surname"]=>
    string(6) "Riddle"
}

第一个函数 sort 是按字母顺序排列数值。此外,如果检查键值,现在它们是列表中的数字,而不是原来的键值。相反,asort 会以同样的方式对值进行排序,但保留了键值之间的关联。最后,ksort 按照键值的字母顺序排列元素。

如何记住这么多函数名

PHP 有很多函数助手,可以帮你省去自己编写自定义函数的麻烦,例如,它提供了多达 13 种不同的排序功能。您还可以随时查阅官方文档。当然,你也希望在编写代码时不需要来回查阅文档。因此,这里有一些提示,可以帮助你记住每个排序功能的作用:

  • 名称中的 a 表示关联,因此将保留键值关联。

  • 名称中的 r 表示反向,因此排序将从高到低。

  • k 表示键,因此排序将基于键而不是值。

其它数组函数

与数组相关的函数大约有 80 种。可以想象,其中有些函数你甚至从未听说过,因为它们有非常特殊的用途。完整的列表可以在 http://php.net/manual/en/book.array.php 上找到。

我们可以用 array_keys 获取数组的键值列表,用 array_values 获取数组的值列表:

<?php
$properties = [
    'firstname' => 'Tom',
    'surname' => 'Riddle',
    'house' => 'Slytherin'
];
$keys = array_keys($properties);
var_dump($keys);
$values = array_values($properties);
var_dump($values);

我们可以使用 count 函数获取数组中元素的个数:

<?php
$names = ['Harry', 'Ron', 'Hermione'];
$size = count($names);
var_dump($size); // 3

我们还可以使用 array_merge 将两个或多个数组合并为一个数组:

<?php
$good = ['Harry', 'Ron', 'Hermione'];
$bad = ['Dudley', 'Vernon', 'Petunia'];
$all = array_merge($good, $bad);
var_dump($all);

最后一个示例将打印以下数组:

array(6) {
    [0]=>
    string(5) "Harry"
    [1]=>
    string(3) "Ron"
    [2]=>
    string(8) "Hermione"
    [3]=>
    string(6) "Dudley"
    [4]=>
    string(6) "Vernon"
    [5]=>
    string(7) "Petunia"
}

正如你所看到的,第二个数组的键值现在不同了,因为最初两个数组的数字键值相同,而一个数组不能有两个相同键值的值。