2012-05-21

Java 腦袋學 PHP Array

PHP Array 分成兩種:傳統的數值陣列(Numerical Array),以及像是 Java Map 的關聯陣列(Associative Array),前者以數值為 key,後者以字串為 key;在 PHP 裡,兩種 Array 可以混用,也就是說數值陣列同時可以是關聯陣列,關聯陣列也同時可以是數值陣列,簡單說,就是一個陣列裡可以同時存在數值 key 與字串 key,但不建議找麻煩這麼混合使用。

Create

建立一個空陣列
$array = array();
建立一個陣列,並同時指派 value
$array = array('a', 'b', 'c');

或者

$array[0] = 'a';
$array[1] = 'b';
$array[2] = 'c';

又或者

$array[] = 'a';
$array[] = 'b';
$array[] = 'c';

後面兩種方式,當 PHP 第一次遇到未定義的變數,就會立即建立該變數,所以在 PHP 裡,變數是不用預先定義的,但這是在指定值時使用未定義的變數才有用,若是在運算時使用未定義的變數就不行了,會丟出「Notice: Undefined variable:...」這樣的訊息。

建立一個自訂數值 key 的陣列
$array = array(1 => 'a', 3 => 'b', 5 => 'c');

或者

$array[1] = 'a';
$array[3] = 'b';
$array[5] = 'c';
建立一個關聯陣列
$array = array('A' => 'a', 'B' => 'b', 'C' => 'c');

或者

$array['A'] = 'a';
$array['B'] = 'b';
$array['C'] = 'c';
多維陣列,可以在一個位置放多個 value
$array['a'][] = 'a0';
$array['a'][] = 'a1';
$array['a'][] = 'a2';
$array['b'][] = 'b0';
移除重複的 value(Java Set)
$new_array = array_unique($array);

或者新建中的數值陣列

if (!in_array($array, 'value')) {
$array[] = 'value';
}

或者新建中的關聯陣列

$array['a'] = 'a';
array_unique 適用於已經存在的陣列,若是新建中的數值陣列可以用 in_array 判斷是否有相同的 value 存在,至於關聯陣列,則用相同的 key 與 value 即可。

不管是數值陣列或關聯陣列,已完成的陣列用 array_unique 效能最好,而新建中的陣列則是先用上述關聯陣列的作法,最後再用 array_values 來得到一個數值陣列,效能最好,也可以用 array_keys,但是效能略遜於 array_values。

Read

判斷某一個物件是不是陣列
if (is_array($array)) { ... }
取得陣列裡的 value 數量
$cnt = count($array);
將數值陣列裡的每個 value 取出
list($a, $b, $c, $d) = array('a', 'b', 'c');;

上述語法等於

$a = $array[0];
$b = $array[1];
$c = $array[2];
$d = $array[3]; // 出錯
由上可知,list 只適用於數值陣列,且 list 裡的參數由 0 開始,依序往上加,因此如果遇到不連續數值陣列或超出數值 key 值,就會出錯「Notice: Undefined offset: 3...」。

迴圈讀取陣列
foreach ($array as $value) {
print "Value: $value";
}

或者

foreach ($array as $key => $value) {
print "Key: $key, Value: $value";
}

又或者

for ($i = 0, $cnt = count($array); $i < $cnt; $i++) {
print($array[$i]);
}

再或者

reset($array);
while (list($key, $value) = each($array)) {
print "Key: $key, Value: $value";
}
用 foreach 方式讀取數值陣列,所得到的 $key 為 key 值;用 for 方式讀取數值陣列,要小心漏洞問題,參考稍後的 Delete 說明。

each 需搭配 reset 使用,因為 each 每次呼叫會將 cursor 往下移到一個位置,然後回傳一個新的陣列,該陣列有四個 key 值,分別為 0、1、key 與 value,0 同 key,1 同 value,因為有 0 與 1,所以可以使用 list 去接;reset 則是將 cursor 移回起點。

foreach 會先複製一份陣列,然後對複製的陣列做迴圈,而 for 與 while 則是直接對原陣列做迴圈,所以當在迴圈中修改陣列時要特別注意。

不管哪一種迴圈方式,直接對 $key 或 $value 做修改並不會影響原陣列,得用 $array($key) = 'new value' 才有用。

將陣列轉為字串
$string = join(',', $array);
檢查陣列中有無某個 key
if (array_key_exists('key', $array) { ... }

或者

if (isset($array['key']) { ... }
array_key_exists 只管 key 在不在,不管 value 為何;而 isset 不只看 key 在不在,value 還必須不是 null 才行。

檢查陣列中有無某個 value
if (in_array('value', $array) { ... }
in_array 預設使用 == 來判斷 value 是否相等,可以加上第三個參數 true 來使用 === 判斷相等與否,以避免奇怪的狀況發生。

翻轉陣列中的 key 與 value
$new_array = array_flip($array);
array_flip 以原陣列 value 為 key,以 key 為 value 做翻轉,用處是「檢查陣列中有無某個 key」的 isset 效能優於「檢查陣列中有無某個 value」的 in_array,所以當要檢查某個大陣列中有無指定的 value 時,可以先進行翻轉,再用 isset 檢查有無指定的 key,效能優於 in_array;但是使用 array_flip 要小心原陣列可能有多個同樣的 value 造成新陣列的 value 是原陣列同 value 的最後一個 key 值。

取得陣列中某個 value 的 key 值
$key = array_search($value, $array);
array_search 如果沒找到,則會回傳 false,如果指定的 value 在陣列中出現多次,則會回傳其中一個 key 值,不一定是第一個。

Update

加入一個 value 到陣列的最後面
 $array[] = 'd';

或者

array_push($array, 'd');
取出陣列第一個 value 並回傳
$array = array('a', 'b', 'c');
$a = array_shift($array);
// $a 為 'a',$array 為 Array ( [0] => b [1] => c )
取出陣列最後一個 value 並回傳
$a = array_pop($array);
// $a 為 'c',$array 為 Array ( [0] => a [1] => b )
array_shiftarray_pop 都會重新計算 key 值,以避免漏洞問題,參考稍後的 Delete 說明。

加大陣列
$array = array_pad($array, 4, '');
array_pad 建立新的陣列,並未更動原陣列,另外可以用負值進行 Left Padding。

縮小陣列
array_splice($array, $start);
參考稍後的 Delete 說明。

排序陣列
sort($array);

或者

sort($array, SORT_NUMERIC);
sort 預設以文字方式排序,可以加上第二個參數設定為數值排序,但是 sort 會重新計算 key 值,不管是數值陣列或關聯陣列;可以用 asort 來保留 key 與 value 的關係,多半用在關聯陣列上。

但如果 value 值為文字與數字混合的話,要改用 natsort

若要反向排序,可以用 rsortarsort,但是 natsort 無法反向排序,但是有不管大小寫的排序方式 natcasesort

若要用自訂的排序機制,可以用 usort。
function aSort($a, $b) {
return 1; // or 0 or -1
}
usort($array, 'aSort');

Delete

移除一個 value
unset($array[4]);

或者

unset($array['a']);
移除不連續多個 value
unset($array[4], $array[6]);

或者

unset($array['a'], $array['c']);
unset 會清掉 key 值,因而造成 for 迴圈讀取數值陣列的漏洞問題,因為 PHP 數值陣列允許不連續 key 的存在,不管是建立造成,如下式一,又或者 unset 造成,如下式二,因為 for 需要用到 count 來取得陣列的數值數量,此時式一與式二陣列的 count 都會得到 3,count 計算的是數量,並不是最大 key 值,所以用 for + count 的方式讀取以下陣列時,會在 key[1] 出錯,且沒有讀到key[3]。
$array = array(0 => 'a', 2 => 'c', 3 => 'd');

$array = array('a', 'b', 'c', 'd'); 
unset($array[1]);

for ($i = 0, $cnt = count($array); $i < $cnt; $i++) {
print($array[$i]);
}
為保留 key 值,可以將 value 設為空字串或者 null:
$array[1] = '';
$array[2] = null;

或者使用 array_values 重新計算 key 值:
$array = array_values($array);
移除連續多個 value
$new_array = array_splice($array, $start, $length);
array_splice 對原陣列動刀,並將取出的片段以陣列形成回傳,且重新計算原陣列的 key 值以避免漏洞問題;$start 用正值表示從前面算起,也可以用負值從後面算起,方便於截掉後面幾個 value。


Advance

對陣列裡的所有 value 執行同一個 function
$new_array = array_map('strtoupper', $array);
除了 array_map,另外還有 array_walk 可以用。

產生一個連續數值陣列
$a = range(1, 3); // 1, 2, 3

$b = range(1, 5, 2); // 1, 3, 5
range 三個參數分別為 $start, $end, $step

陣列二合一
$array = array_merge(array('a', 'b'), array('c'));

或者

$array = array('a', 'b') + array('c');
array_merge 為將兩個數值陣列所有的 value 保留下來,會重新計算 key,不只第二個陣列重新計算 key,第一個陣列也會重新計算;若是關聯陣列,若有相同的 key 值,則會後蓋前。

若是用 + 號執行陣列二合一,則不會重新計算 key,不管是數值陣列或關聯陣列,一律後蓋前。

過濾陣列
function aFunction($obj) {
return true; // or false
}
$new_array = array_filter($array, 'aFunction');
也可以使用 foreach 達成相同的效果,甚至 foreach 可以中途退出,不過以可讀性而言,array_filter 的目的不言自明。

取得陣列中最大或最小的 value
$max = max($array);

$min = min($array);
反轉陣列
array_reverse($array);
如果可以的話,用 for 從最大 key 值讀到最小 key 值就有反轉的效果,但是前提是 key 值不可以有漏洞。

搞亂陣列
shuffle($array);
聯集陣列
$u = array_unique(array_merge($a, $b));

交集陣列

$i = array_intersect($a, $b);
差集陣列
$d = array_diff($a, $b); // 單向差集

或者雙向差集

$d = array_merge(array_diff($a, $b), array_diff($b, $a));
array_diff($a, $b) 只取得 $a 有而 $b 沒有的集合。

沒有留言:

張貼留言