PHP 陣列使用之道

liuqing_hu發表於2018-07-09

本文首發於 PHP 陣列使用之道,轉載請註明出處。

這個教程我將通過一些實用的例項和最佳實踐的方式列舉出 PHP 中常用的陣列函式。每個 PHP 工程師都應該掌握它們的使用方法,以及如何通過組合使用來編寫更精簡且易讀的程式碼。

另外,我們提供了相關示例程式碼的簡報,你可以從相關連結下載它,並分享給你的團隊來打造更強的團隊。

入門

先讓我們從一些處理陣列鍵名和鍵值的基礎陣列函式開始。array_combine() 作為陣列函式中的一員,用於通過使用一個陣列的值作為其鍵名,另一個陣列的值作為其值來建立一個全新陣列:

<?php
$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_flip() 函式,它的功能則是交換陣列中的鍵值和鍵名:

<?php

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() 函式,確切的說它不是一個函式,而是一種語言結構,可以在單次操作中將陣列中的值賦值給一組變數。舉個例子,下面給出 list() 函式的基本使用:

<?php
// 定義陣列
$array = ['a', 'b', 'c'];

// 不使用 list()
$a = $array[0];
$b = $array[1];
$c = $array[2];

// 使用 list() 函式
list($a, $b, $c) = $array;

這個語言結構結合 preg_split()explode() 這類函式使用效果更佳,如果你無需定義其中的某些值,可以直接跳過一些引數的賦值:

$string = 'hello|wild|world';

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

另外,list() 還可用於 foreach 遍歷,這種用法更能發揮這個語言結構的優勢:

$arrays = [[1, 2], [3, 4], [5, 6]];

foreach ($arrays as list($a, $b)) {
    $c = $a + $b;

    echo $c, ', ';
}

譯者注:list() 語言結構僅適用於數字索引陣列,並預設索引從 0 開始,且無法用於關聯陣列,檢視 文件

而通過使用 extract() 函式,你可以將關聯陣列匯出到變數(符號表)中。對陣列中的各個元素,將會以其鍵名作為變數名建立,變數的值則為對應元素的值:

<?php
$array = [
    'clothes' => 't-shirt',
    'size' => 'medium',
    'color' => 'blue',
];

extract($array);

echo $clothes, ' ', $size, ' ', $color;

注意在處理使用者資料(如請求的資料)時 extract() 函式是一個安全的函式,所以此時最好使用更好的 標誌型別EXTR_IF_EXISTSEXTR_PREFIX_ALL

extract() 函式的逆操作是 compact() 函式,用於通過變數名建立關聯陣列:

<?php
$clothes = 't-shirt';
$size = 'medium';
$color = 'blue';

$array = compact('clothes', 'size', 'color');
print_r($array);

// Array
// (
//     [clothes] => t-shirt
//     [size] => medium
//     [color] => blue
// )

過濾函式

PHP 提供一個用於過濾陣列的超讚的函式,它是 array_filter()。將待處理陣列作為函式的第一個引數,第二個引數是一個匿名函式。如果你希望陣列中的元素通過驗證則在匿名函式返回 true,否則返回 false

<?php

$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_USE_KEYARRAY_FILTER_USE_BOTH 作為第三引數指定是否將陣列的鍵值或將鍵值和鍵名同時作為回撥函式的引數。

你還可以不在 array_filter() 函式中定義回撥函式以刪除空值:

<?php
$numbers = [-1, 0, 1];

$not_empty = array_filter($numbers);

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

你可以使用 array_unique() 函式用於從陣列中獲取唯一值元素。注意該函式會保留唯一元素在原陣列中的鍵名:

<?php
$array = [1, 1, 1, 1, 2, 2, 2, 3, 4, 5, 5];

$uniques = array_unique($array);

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

array_column() 函式可以從多維陣列(multi-dimensional)中獲取指定列的值,如從 SQL 資料庫中獲取答案或者 CSV 檔案匯入資料。只需要傳入陣列和指定的列名:

<?php
$array = [
    ['id' => 1, 'title' => 'tree'],
    ['id' => 2, 'title' => 'sun'],
    ['id' => 3, 'title' => 'cloud'],
];

$ids = array_column($array, 'id');

print_r($ids);// [1, 2, 3]

從 PHP 7 開始,array_column 功能更加強大,因為它開始支援 包含物件的陣列,所以在處理陣列模型時變得更加容易:

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

陣列遍歷處理

通過使用 array_map(),你可以對陣列中的每個元素執行回撥方法。你可以基於給定的陣列傳入函式名稱或匿名函式來獲取一個新陣列:

<?php
$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]

對於這個函式還有個謠言,無法同時將陣列的鍵名和鍵值傳入到回撥函式,但是我們現在要來打破它:

<?php
$model = ['id' => 7, 'name' => 'James'];
$res = array_map(function ($key, $value) {
    return $key . ' is ' . $value;
}, array_keys($model), $model);

print_r($res);
// Array
// (
//     [0] => id is 7
//     [1] => name is James
// )

不過這樣處理起來實在是醜陋。最好使用 array_walk() 函式來替代。這個函式表現上和 array_map() 類似,但是工作原理完全不同。第一,陣列是以引用傳值方式傳入,所以 array_walk() 不會建立新陣列,而是直接修改原陣列。所以作為源陣列,你可以將陣列的值以引用傳遞方法傳入回撥函式,陣列的鍵名直接傳入就好了:

<?php
$fruits = [
    'banana' => 'yellow',
    'apple' => 'green',
    'orange' => 'orange',
];

array_walk($fruits, function (&$value, $key) {
    $value = $key . ' is ' . $value;
});

print_r($fruits);

陣列連線操作

在 PHP 中合併陣列的最佳方式是使用 array_merge() 函式。所有的陣列選項會合併到一個陣列中,具有相同鍵名的值會被最後一個值所覆蓋:

<?php
$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_merge() 函式的功能類似都可以完成合並陣列運算,但是結果有所不同,可以檢視 PHP 合併陣列運算子 + 與 array_merge 函式 瞭解更多細節。

為了實現從陣列中刪除不在其他陣列中的值(譯註:計算差值),使用 array_diff()。還可以通過 array_intersect() 函式獲取所有陣列都存在的值(譯註:獲取交集)。接下來的示例演示它們的使用方法:

<?php
$array1 = [1, 2, 3, 4];
$array2 = [3, 4, 5, 6];

$diff = array_diff($array1, $array2);
$intersect = array_intersect($array1, $array2);

print_r($diff); // 差集 [0 => 1, 1 => 2]
print_r($intersect); //交集 [2 => 3, 3 => 4]

陣列的數學運算

使用 array_sum() 對陣列元素進行求和運算,array_product 對陣列元素執行乘積運算,或者使用 array_reduce() 處理自定義運算規則:

<?php

$numbers = [1, 2, 3, 4, 5];

print_r(array_sum($numbers));// 15

print_r(array_product($numbers));// 120

print_r(array_reduce($numbers, function ($carry, $item) {
    return $carry ? $carry / $item : 1;
}));// 0.0083 = 1/2/3/4/5

為了實現統計陣列中值的出現次數,可以使用 array_count_values() 函式。它將返回一個新陣列,新陣列鍵名為待統計陣列的值,新陣列的值為待統計陣列值的出現次數:

<?php

$things = ['apple', 'apple', 'banana', 'tree', 'tree', 'tree'];
$values = array_count_values($things);

print_r($values);

// Array
// (
//     [apple] => 2
//     [banana] => 1
//     [tree] => 3
// )

生成陣列

需要以給定值生成固定長度的陣列,可以使用 array_fill() 函式:

<?php
$bind = array_fill(0, 5, '?');
print_r($bind);

根據範圍建立陣列,如小時或字母,可以使用 range() 函式:

<?php
$letters = range('a', 'z');
print_r($letters); // ['a', 'b', ..., 'z']

$hours = range(0, 23);
print_r($hours); // [0, 1, 2, ..., 23]

為了實現獲取陣列中的部分元素 - 比如,獲取前三個元素 - 使用 array_slice() 函式:

<?php
$numbers = range(1, 10);
$top = array_slice($numbers, 0, 3);

print_r($top);// [1, 2, 3]

排序陣列

首先謹記 PHP 中有關排序的函式都是 引用傳值 的,排序成功返回 true 排序失敗返回 false。排序的基礎函式是 sort() 函式,它執行排序後的結果不會保留原索引順序。排序函式可以歸類為以下幾類:

  • a 保持索引關係進行排序
  • k 依據鍵名排序
  • r 對陣列進行逆向排序
  • u 使用使用者自定義排序規則排序

你可以從下表看到這些排序函式:

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

陣列函式的組合使用

陣列處理的藝術是組合使用這些陣列函式。這裡我們通過 array_filter()array_map() 函式僅需一行程式碼就可以完成空字元擷取和去空值處理:

<?php
$values = ['say', '  bye', '', ' to', ' spaces  ', '    '];
$words = array_filter(array_map('trim', $values));

print_r($words);// ['say', 'bye', 'to', 'spaces']

依據模型陣列建立 id 和 title 資料字典,我們可以結合使用 array_combine()array_column() 函式:

<?php
$models = [$model, $model, $model];

$id_to_title = array_combine(
    array_column($models, 'id'),
    array_column($models, 'title')
);

print_r($id_to_title);

譯註:提供一個 可執行的版本

為了實現獲取出現頻率最高的陣列元素,我們可以使用 array_count_values()arsort()array_slice() 這幾個函式:

<?php

$letters = ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'd', 'd', 'd', 'd', 'd'];

$values = array_count_values($letters);
arsort($values);
$top = array_slice($values, 0, 3);

print_r($top);

還可以輕易的通過 array_sum()array_map() 函式僅需數行就能完成計算訂單的價格:

<?php
$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 開發者應該掌握的最基本的一些。

另外需要注意的是我們建立了這些函式的示例,所以你可以從相關小節下載和分享給你的團隊。

如果你有任何問題,不要猶豫直接在文章的評論表單發表出來就好了。

更多及相關連結

原文

Working With PHP Arrays in the Right Way

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章