巧用 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() 先将值做为字符串排序,而后对每一个值只保留第一个遇到的键名,接着忽略全部后面的键名。工具

由于这个函数会先将数组进行排序,因此速度可能在某些场景达不到预期的要求。post

如今咱们能够祭出咱们的黑科技 array_flip 函数,众所周知 PHP 里数组的键名是惟一的,因此在键名和值对调后重复的值便被忽略了。试想一下咱们连续调用两次 array_flip 函数是否是就至关于实现了 array_unique 函数的功能呢?示例代码以下:学习

<?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

项目地址:github.com/zanemmm/uti…

0x0A 结语

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

个人博客原文

相关文章
相关标签/搜索