首頁 >後端開發 >php教程 >如何巧用 PHP 數組函數

如何巧用 PHP 數組函數

藏色散人
藏色散人轉載
2020-03-09 16:14:152367瀏覽

0x00 前言

PHP 的陣列是一種很強大的資料類型,同時PHP 內建了一系列與陣列相關的函數可以輕易的實現日常開發的功能。但我發現好像很多小夥伴都忽略了內建函數的作用(例如我自己就編寫過一些有關數組操作的程式碼然後發現PHP自帶了~~,善用PHP 內建函數能極大的提高開發效率和運行效率(內建函數都是用C 寫的效率比用PHP 寫的高很多),所以本文便總結了一些在常見場景中利用PHP 內建函數的實現方法。此外如果想更深入的學習有關PHP 數組函數最好還是去查PHP 手冊!

相關學習推薦:

PHP影片教學:https://www.php.cn/course /list/29/type/2.html

0x01 取指定鍵名

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

<?php
$raw = [&#39;id&#39; => 1, &#39;name&#39; => &#39;zane&#39;, &#39;password&#39; => &#39;123456&#39;];
// 自己用 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, [&#39;id&#39;, &#39;name&#39;]));
// 结果 [&#39;id&#39; => 1, &#39;name&#39; => &#39;zane&#39;]
var_dump(newOnlyKeys($raw, [&#39;id&#39;, &#39;name&#39;]));
// 结果 [&#39;id&#39; => 1, &#39;name&#39; => &#39;zane&#39;]

很明顯簡潔很多有木有!不過array_intersect_key 和array_flip 是什麼鬼?這裡簡單的介紹一下這兩個函數的作用,首先是array_flip函數,這個函數的功能是「將數組的鍵和值對調」,也就是鍵名變成值,值變成鍵名。我們傳遞的$keys 參數經過這個函數便從[0 => 'id' , 1 => 'name'] 轉變為了['id' => 0, 'name' => 1]。

這樣做的目的是為了向array_intersect_key 函數服務,array_intersect_key 函數的功能是「使用鍵名比較計算數組的交集」,也就是返回第一個參數數組中與其他參數數組相同鍵名的值。這樣便實現了取指定鍵名的功能~(≧▽≦)/~啦!當然要詳細了解這兩個函數的功能還是要查PHP 官方手冊:array_flip array_intersect_key

0x02 移除指定鍵名

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

<?php
$raw = [&#39;id&#39; => 1, &#39;name&#39; => &#39;zane&#39;, &#39;password&#39; => &#39;123456&#39;];
// 用 PHP 内置函数实现
function removeKeys($array, $keys) {
    return array_diff_key($array, array_flip($keys));
}
// 移除 id 键
var_dump(removeKeys($raw, [&#39;id&#39;, &#39;password&#39;]));
// 结果 [&#39;name&#39; => &#39;zane&#39;]

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

0x03 陣列去重

這個相信大家都有這個需求,當然PHP 也內建了array_unique 函數供給大家使用,如下例:

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

嘿,用這個函數就能解決大部分問題了,但是有時候你可能會覺得它不夠快,原因如下:

array_unique() 先將值作為字串排序,然後每個值只保留第一個遇到的鍵名,接著忽略所有後面的鍵名。

因為這個函數會先將陣列排序,所以速度可能在某些場景達不到預期的要求。

現在我們可以祭祀我們的黑科技 array_flip 函數,眾所周知 PHP 裡數組的鍵名是唯一的,所以在鍵名和值對調後重複的值便被忽略了。試想我們連續呼叫兩次 array_flip 函數是不是就等於實作了 array_unique 函數的函數呢?範例程式碼如下:

<?php
$input = [&#39;you are&#39; => 666, &#39;i am&#39; => 233, &#39;he is&#39; => 233, &#39;she is&#39; => 666];
$result = array_flip(array_flip($input));
var_dump($result);
// 结果 [&#39;she is&#39; => 666, &#39;he is&#39; => 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 = [&#39;hello&#39; => &#39;world&#39;, 0 => 233, 99 => 666];
var_dump(array_slice($input, 0));
// 结果 [&#39;hello&#39; => &#39;world&#39;, 0 => 233, 1 => 66]

array_slice 函數的功能是取出數組的中的一段,但它預設會重新排序並重置數組的數字索引,所以可以利用它重置數組中的數字索引。

0x05 清除空值

#

嘿,有时候我们想清除某个数组中的空值比如:null、false、0、0.0、[]空数组、''空字符串、'0'字符串0 ,这时 array_filter 函数便能帮上大忙。代码如下:

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

为什么会出现这样的结果捏?array_filter 的作用其实是「用回调函数过滤数组中的单元」,它的第二个参数其实是个回调函数,向数组的每个成员都执行这个回调函数,若回调函数的返回值为 true 便保留这个成员,为 false 则忽略。这个函数还有一个特性就是:

如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。

等值为 false 就是转换为 bool 类型后值为 false 的意思,详细看文档:转换为布尔类型。

注意:如果不填写 callback 函数,0、0.0、'0'字符串0 这些可能有意义的值会被删除。所以如果清除的规则有所不同还需要自行编写 callback 函数。

0x06 确认数组成员全部为真

有时候我们希望确认数组中的的值全部为 true,比如:['read' => true, 'write' => true, 'execute' => true],这时我们需要用一个循环判定吗?NO,NO,NO……只需要用 array_product 函数便可以实现了。代码如下:

<?php
$power = [&#39;read&#39; => true, &#39;write&#39; => true, &#39;execute&#39; => true];
var_dump((bool)array_product($power));
// 结果 true
$power = [&#39;read&#39; => true, &#39;write&#39; => true, &#39;execute&#39; => false];
var_dump((bool)array_product($power));
// 结果 false

为什么能实现这个功能呢? array_product 函数本来的功能是「计算数组中所有值的乘积」,在累乘数组中所有成员的时候会将成员的值转为数值类型。当传递的参数为一个 bool 成员所组成的数组时,众所周知 true 会被转为 1,false 会被转为 0。然后只要数组中出现一个 false 累乘的结果自然会变成 0,然后我们再将结果转为 bool 类型不就是 false 了嘛!

注意:使用 array_product 函数将在计算过程中将数组成员转为数值类型进行计算,所以请确保你了解数组成员转为数值类型后的值,否则会产生意料之外的结果。比如:

<?php
$power = [&#39;read&#39; => true, &#39;write&#39; => true, &#39;execute&#39; => &#39;true&#39;];
var_dump((bool)array_product($power));
// 结果 false

上例是因为 'true' 在计算过程中被转为 0。要想详细了解请点击这里。

0x07 获取指定键名之前 / 之后的数组

如果我们只想要关联数组中指定键名值之前的部分该怎么办呢?又用一个循环?当然不用我们可以通过 array_keys、array_search 和 array_slice 组合使用便能够实现!下面贴代码:

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

思路解析,要实现这样的功能大部分同学都应该能想到 array_slice 函数,但这个函数取出部分数组是根据偏移量(可以理解为键名在数组中的顺序,从 0 开始)而不是根据键名的,而关联数组的键名却是是字符串或者是不按顺序的数字,此时要解决的问题便是「如何取到键名对应的偏移量?」,这是 array_keys 函数便帮了我们大忙,它的功能是「返回数组中部分的或所有的键名」默认返回全部键名,此外返回的键名数组是以数字索引的,也就是说返回的键名数组的索引就是偏移量!例子中的原数组变为: [0 => 'first', 1 => 'second', 2 => 'third'] 。然后我们通过 array_search 便可以获得指定键名的偏移量了,因为这个函数的功能是「在数组中搜索给定的值,如果成功则返回首个相应的键名」。有了偏移量我们直接调用 array_slice 函数便可以实现目的了。

上面的例子懂了,那获取指定键名之后的数组也就轻而易举了,略微修改 array_slice 即可。直接贴代码:

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

那如何获取指定值之前或之后的数组呢?嘿,记得 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。

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

0x0A 结语

其实还有很多实用的函数没有介绍,但是限于文章篇幅就讲到这里了吧。本文出现的很多例子都并非本人原创的,多数出于 PHP 官方手册(每个函数功能下面的评论里都有很多大神提出一些厉害的用法,部分示例就是出自评论)。在下只是拾人牙慧,将其总结了一下。另外文章中若出现错误,希望大家能够指出,若有疑问可以互相讨论。

更多编程相关内容,请关注php中文网编程入门栏目!

以上是如何巧用 PHP 數組函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除