巧用 PHP 數組函數

0x00 前言

PHP 的數組是一種很強大的數據類型,與此同時 PHP 內置了一系列與數組相關的函數能夠很輕易的實現平常開發的功能。可是我發現好像不少小夥伴都忽略了內置函數的做用(好比我本身就編寫過一些有關數組操做的代碼而後發現PHP自帶了/(ㄒoㄒ)/~~),善用 PHP 內置函數能極大的提升開發效率和運行效率(內置函數都是用 C 寫的效率比用 PHP 寫的高不少),因此本文便總結了一些在常見場景中利用 PHP 內置函數的實現方法。此外若是想更深刻的學習有關 PHP 數組函數最好仍是去查 PHP 手冊!點我看官方數組函數手冊php

0x01 取指定鍵名

對於某些關聯數組,有時候咱們只想取指定鍵名的那部分,好比數組爲 ['id' => 1, 'name' => 'zane', 'password' => '123456'] 此時若只想取包含 id 和 name 的部分該怎麼實現呢?下面直接貼代碼。html

<?php
$raw = ['id' => 1, 'name' => 'zane', 'password' => '123456'];
// 本身用 PHP 實現
function onlyKeys($raw, $keys) {
    $new = [];
    foreach ($raw as $key => $val) {
        if (in_array($key, $keys)) {
            $new[$key] = $val;
        }
    }
    
    return $new;
}
// 用 PHP 內置函數實現
function newOnlyKeys($array, $keys) {
    return array_intersect_key($array, array_flip($keys));
}
var_dump(onlyKeys($raw, ['id', 'name']));
// 結果 ['id' => 1, 'name' => 'zane']
var_dump(newOnlyKeys($raw, ['id', 'name']));
// 結果 ['id' => 1, 'name' => 'zane']

很明顯簡潔不少有木有!不過 array_intersect_keyarray_flip 是什麼鬼?這裏簡單的介紹一下這兩個函數的做用,首先是 array_flip 函數,這個函數的功能是「將數組的鍵和值對調」,也就是鍵名變成值,值變成鍵名。咱們傳遞的 $keys 參數通過這個函數便從 [0 => 'id', 1 => 'name'] 轉變爲了 ['id' => 0, 'name' => 1]。這樣作的目的是爲了向 array_intersect_key 函數服務,array_intersect_key 函數的功能是「使用鍵名比較計算數組的交集」,也就是返回第一個參數數組中與其餘參數數組相同鍵名的值。這樣便實現了取指定鍵名的功能 ~(≧▽≦)/~啦!固然要詳細瞭解這兩個函數的功能仍是要查 PHP 官方手冊:array_flip array_intersect_keygit

0x02 移除指定鍵名

有了上一個例子作鋪墊,這個就簡單講講啦,道理是大同小異滴。github

<?php
$raw = ['id' => 1, 'name' => 'zane', 'password' => '123456'];
// 用 PHP 內置函數實現
function removeKeys($array, $keys) {
    return array_diff_key($array, array_flip($keys));
}
// 移除 id 鍵
var_dump(removeKeys($raw, ['id', 'password']));
// 結果 ['name' => 'zane']

和上一個例子相比本例只是將 array_intersect_key 函數改成 array_diff_key,嗯……相信你們能猜出來這個函數的功能「使用鍵名比較計算數組的差集」,恰好和 array_intersect_key 的功能相反而已。官方手冊:array_diff_key面試

0x03 數組去重

這個相信你們都有這個需求,固然 PHP 也內置了 array_unique 函數供給你們使用,以下例:數組

<?php
$input = ['you are' => 666, 'i am' => 233, 'he is' => 233, 'she is' => 666];
$result = array_unique($input);
var_dump($result);
// 結果 ['you are' => 666, 'i am' => 233]

嘿,用這個函數就能解決大部分問題了,可是有時候你可能會以爲它不夠快,緣由以下:函數

array_unique() 先將值做爲字符串排序,而後對每一個值只保留第一個遇到的鍵名,接着忽略全部後面的鍵名。

由於這個函數會先將數組進行排序,因此速度可能在某些場景達不到預期的要求。工具

如今咱們能夠祭出咱們的黑科技 array_flip 函數,衆所周知 PHP 裏數組的鍵名是惟一的,因此在鍵名和值對調後重復的值便被忽略了。試想一下咱們連續調用兩次 array_flip 函數是否是就至關於實現了 array_unique 函數的功能呢?示例代碼以下:post

<?php
$input = ['you are' => 666, 'i am' => 233, 'he is' => 233, 'she is' => 666];
$result = array_flip(array_flip($input));
var_dump($result);
// 結果 ['she is' => 666, 'he is' => 233]

嗯哼?!結果和 array_unique 的不同!爲何,咱們能夠從 PHP 官方手冊獲得答案:學習

若是同一個值出現屢次,則最後一個鍵名將做爲它的值,其它鍵會被丟棄。

總的來講就是 array_unique 保留第一個出現的鍵名,array_flip 保留最後一個出現的鍵名。

注意:使用 array_flip 做爲數組去重時數組的值必須可以做爲鍵名(即爲 string 類型或 integer 類型),不然這個值將被忽略。

此外,若不須要保留鍵名咱們能夠直接這樣使用 array_values(array_flip($input))

0x04 重置索引

當咱們想要對一個索引並不連續的數組進行重置時,好比數組:[0 => 233, 99 => 666],對於這種數組咱們只須要調用 array_values 函數便可實現。以下例:

<?php
$input = [0 => 233, 99 => 666];
var_dump(array_values($input));
// 結果 [0 => 233, 1 => 66]

須要注意的是 array_values 函數並不止重置數字索引還會將字符串鍵名也一樣刪除並重置。那如何在保留字符串鍵名的同時重置數字索引呢?答案就是 array_slice 函數,代碼示例以下:

<?php
$input = ['hello' => 'world', 0 => 233, 99 => 666];
var_dump(array_slice($input, 0));
// 結果 ['hello' => 'world', 0 => 233, 1 => 66]

array_slice 函數的功能是取出數組的中的一段,但它默認會從新排序並重置數組的數字索引,因此能夠利用它重置數組中的數字索引。

0x05 清除空值

嘿,有時候咱們想清除某個數組中的空值好比:nullfalse00.0[]空數組''空字符串'0'字符串0 ,這時 array_filter 函數便能幫上大忙。代碼以下:

<?php
$input = ['foo', false, -1, null, '', []];
var_dump(array_filter($input));
// 結果 [0 => 'foo', 2 => -1]

爲何會出現這樣的結果捏?array_filter 的做用實際上是「用回調函數過濾數組中的單元」,它的第二個參數實際上是個回調函數,向數組的每一個成員都執行這個回調函數,若回調函數的返回值爲 true 便保留這個成員,爲 false 則忽略。這個函數還有一個特性就是:

若是沒有提供 callback 函數, 將刪除 array 中全部等值爲 FALSE 的條目。

等值爲 false 就是轉換爲 bool 類型後值爲 false 的意思,詳細看文檔:轉換爲布爾類型

注意:若是不填寫 callback 函數,00.0'0'字符串0 這些可能有意義的值會被刪除。因此若是清除的規則有所不一樣還須要自行編寫 callback 函數。

0x06 確認數組成員所有爲真

有時候咱們但願確認數組中的的值所有爲 true,好比:['read' => true, 'write' => true, 'execute' => true],這時咱們須要用一個循環斷定嗎?NO,NO,NO……只須要用 array_product 函數即可以實現了。代碼以下:

<?php
$power = ['read' => true, 'write' => true, 'execute' => true];
var_dump((bool)array_product($power));
// 結果 true
$power = ['read' => true, 'write' => true, 'execute' => false];
var_dump((bool)array_product($power));
// 結果 false

爲何能實現這個功能呢? array_product 函數原本的功能是「計算數組中全部值的乘積」,在累乘數組中全部成員的時候會將成員的值轉爲數值類型。當傳遞的參數爲一個 bool 成員所組成的數組時,衆所周知 true 會被轉爲 1,false 會被轉爲 0。而後只要數組中出現一個 false 累乘的結果天然會變成 0,而後咱們再將結果轉爲 bool 類型不就是 false 了嘛!

注意:使用 array_product 函數將在計算過程當中將數組成員轉爲數值類型進行計算,因此請確保你瞭解數組成員轉爲數值類型後的值,不然會產生意料以外的結果。好比:

<?php
$power = ['read' => true, 'write' => true, 'execute' => 'true'];
var_dump((bool)array_product($power));
// 結果 false

上例是由於 'true' 在計算過程當中被轉爲 0。要想詳細瞭解請點擊這裏

0x07 獲取指定鍵名以前 / 以後的數組

若是咱們只想要關聯數組中指定鍵名值以前的部分該怎麼辦呢?又用一個循環?固然不用咱們能夠經過 array_keysarray_searcharray_slice 組合使用便可以實現!下面貼代碼:

<?php
$data = ['first' => 1, 'second' => 2, 'third' => 3];
function beforeKey($array, $key) {
    $keys = array_keys($array);
      // $keys = [0 => 'first', 1 => 'second', 2 => 'third']
    $len = array_search($key, $keys);
    return array_slice($array, 0, $len);
}
var_dump(beforeKey($data, 'first'));
// 結果 []
var_dump(beforeKey($data, 'second'));
// 結果 ['first' => 1]
var_dump(beforeKey($data, 'third'));
// 結果 ['first' => 1, 'second' => 2]

思路解析,要實現這樣的功能大部分同窗都應該能想到 array_slice 函數,但這個函數取出部分數組是根據偏移量(能夠理解爲鍵名在數組中的順序,從 0 開始)而不是根據鍵名的,而關聯數組的鍵名倒是是字符串或者是不按順序的數字,此時要解決的問題即是「如何取到鍵名對應的偏移量?」,這是 array_keys 函數便幫了咱們大忙,它的功能是「返回數組中部分的或全部的鍵名」默認返回所有鍵名,此外返回的鍵名數組是以數字索引的,也就是說返回的鍵名數組的索引就是偏移量!例子中的原數組變爲: [0 => 'first', 1 => 'second', 2 => 'third'] 。而後咱們經過 array_search 即可以得到指定鍵名的偏移量了,由於這個函數的功能是「在數組中搜索給定的值,若是成功則返回首個相應的鍵名」。有了偏移量咱們直接調用 array_slice 函數即可以實現目的了。

上面的例子懂了,那獲取指定鍵名以後的數組也就垂手可得了,略微修改 array_slice 便可。直接貼代碼:

<?php
$data = ['first' => 1, 'second' => 2, 'third' => 3];
function afterKey($array, $key) {
    $keys = array_keys($array);
    $offset = array_search($key, $keys);
    return array_slice($array, $offset + 1);
}
var_dump(afterKey($data, 'first'));
// 結果 ['second' => 2, 'third' => 3]
var_dump(afterKey($data, 'second'));
// 結果 ['third' => 3]
var_dump(afterKey($data, 'third'));
// 結果 []

那如何獲取指定值以前或以後的數組呢?嘿,記得 array_search 的做用吧,其實咱們只須要這樣調用 beforeKey($data, array_search($value, $data)) 不就實現了嘛!

0x08 數組中重複次數最多的值

敲黑板,劃重點!聽說這是一道面試題喔。假設有這樣一個數組 [6, 11, 11, 2, 4, 4, 11, 6, 7, 4, 2, 11, 8],請問如何獲取數組中重複次數最多的值?關鍵就在於 array_count_values 函數。實例代碼以下:

<?php
$data = [6, 11, 11, 2, 4, 4, 11, 6, 7, 4, 2, 11, 8];
$cv = array_count_values($data);
// $cv = [6 => 2, 11 => 4, 2 => 2, 4 => 3, 7 => 1, 8 => 1]
arsort($cv);
$max = key($cv);
var_dump($max);
// 結果 11

array_count_values 函數的功能是「統計數組中全部的值」,就是將原數組中的值做爲返回數組的鍵名,值出現的次數做爲返回數組的值。這樣咱們即可以經過 arsort 函數對出現的次數進行降序排序而且保持索引關聯。最後使用 key 得到當前單元(當前單元默認爲數組第一個成員)的鍵名,此時的鍵名便是原數組的值重複次數最多的值。

0x09 打廣告時間

雖然 PHP 提供了不少和數組相關的函數,但使用起來仍是不算太方便並且都是經過函數的調用方式而沒有面向對象相關的實現,因此我最近在寫一個開源的工具類項目 zane/utils,封裝了一些經常使用的方法而且支持鏈式調用,其中的 Ary 類實現 「獲取數組中重複次數最多的值」只需一行,以下所示:

$data = [6, 11, 11, 2, 4, 4, 11, 6, 7, 4, 2, 11, 8];
$max = Ary::new($data)->countValues()->maxKey();
var_dump($max);
// 結果 11

歡迎你們給我提 issue 和 pr,另外若是你喜歡這個項目但願動動小手點個 star :-D

項目地址:https://github.com/zanemmm/utils

0x0A 結語

其實還有不少實用的函數沒有介紹,可是限於文章篇幅就講到這裏了吧。本文出現的不少例子都並不是本人原創的,多數出於 PHP 官方手冊(每一個函數功能下面的評論裏都有不少大神提出一些厲害的用法,部分示例就是出自評論)。在下只是拾人牙慧,將其總結了一下。另外文章中若出現錯誤,但願你們可以指出,如有疑問能夠互相討論:-D。

個人博客原文

相關文章
相關標籤/搜索