过渡
最近在开始使用 ThinkPHP 5.1 进行一系列开发工作,因为之前是使用 Laravel 进行开发,像是标题中的这种小问题都在 Laravel 中很容易实现。直接使用 array_first 方法进行查找即可。
快速实现
但是在 ThinkPHP 中 并没有提供类似方法进行快速处理,所以有需要来重复造轮子了?
至此想到的第一个方法就是使用 array_search 不过这个方法中官方提供的方案仅用于简单的一维数组搜索,而且返回的也只是 index 并不是找到的结果,淡然通过 index 我们也可以取出项目来,在 PHP 5.5 带来的新方法 array_column,可以方便的实现二维搜索 在这里的用户笔记 为我们提供了一个小的示例。
$userdb=Array
(
(0) => Array
(
(uid) => '100',
(name) => 'Sandra Shush',
(url) => 'urlof100'
),
(1) => Array
(
(uid) => '5465',
(name) => 'Stefanie Mcmohn',
(pic_square) => 'urlof100'
),
(2) => Array
(
(uid) => '40489',
(name) => 'Michael',
(pic_square) => 'urlof40489'
)
);
$key = array_search(40489, array_column($userdb, 'uid'));
并且赢得了 800+ 的赞赏,到这里可能你会觉得 通过这个方式取到 index 然后用 index 取出来就行了。
一些🌰
但是,如果你再往下翻一下,你会看到另一条用户笔记 ,这条用户笔记告诉我们 当我们使用这种方式来实现二维搜索时你 PHP 版本 必须要在 5.5 + ,作者同时告诉我们
Since array_column() will produce a resulting array; it won’t preserve your multi-dimentional array’s keys. So if you check against your keys, it will fail.
机翻一下 :由于array_column()将产生一个新的数组; 它不会保留多维数组的原来的键。 因此,如果您检查您的键,它将失败。
然后作者也为我们提供了一个🌰
$people = array(
2 => array(
'name' => 'John',
'fav_color' => 'green'
),
5 => array(
'name' => 'Samuel',
'fav_color' => 'blue'
)
);
$found_key = array_search('blue', array_column($people, 'fav_color')); // 1
// Here, you could expect that the $found_key would be "5" but it's NOT. It will be 1. Since it's the second element of the produced array by the array_column() function.
// 机翻一下:在这里,你预期 $found_key 的将是“5”,但它不是,它将是1.因为它是array_column()函数生成的数组的第二个元素。
// 另外 作者还提到了
// Secondly, if your array is big, I would recommend you to first assign a new variable so that it wouldn't call array_column() for each element it searches. For a better performance, you could do;
// 机翻一下:其次,如果您的数组很大,我建议您先分配一个新变量,这样它就不会为它搜索的每个元素调用array_column()。 为了获得更好的性能,你可以做到;
$colors = array_column($people, 'fav_color');
$found_key = array_search('blue', $colors);
看完了这些提示,你已经发现了这其中的坑,然而,这并没有结束,因为如果数据不够纯净的话,你用 array_search
实现的功能可能就只局限于 in_array
。
而且,尽管到了这里,还会遇到另外一个坑,先看🌰
<?php
$userdb = array(
0 => array(
'uid' => 100,
'name' => 'Sandra Shush',
'url' => 'urlof100'
),
'8' => array(
'uid' => 5465,
'name' => 'Stefanie Mcmohn',
'pic_square' => 'urlof100'
),
'3' => Array(
// 'uid' => 5555,
'name' => 'Michael',
'pic_square' => 'urlof40489'
),
'6' => Array(
'uid' => 40489,
'name' => 'Michael',
'pic_square' => 'urlof40489'
)
);
$found_key = array_search(40489, array_column($userdb, 'uid'));
在这里 猜想一下 $found_key 会是什么?答案是: 2
。
??? ,因为当在执行 array_column() 时,第三个元素,也就是键为3的数据中 uid 被注释了,这时候 PHP 就会忽略它,并不会被保留索引位置,所以这时候结果只有 3 个元素,第四个向上替补了,因为数组索引是 0 开始,所以 2 就相当于第 3 个元素了。
大家都是怎么做的?
在 array_search
的用户笔记区中,看到了很多都是数组循环并比对后返回,比如这个,但是这样又遇到一些局限性问题,这时候我们又想到了 Laravel 中把自主权交给调用者,由调用者在匿名方法内进行处理并返回 bool 值 进行处理。
至此,看回 Laravel 的 array_fisrt 方法,通过 Laravel 源码可以看到 array_first 是 Arr::fisrt 方法的一个包装,在这里我们可以看到 Laravel 的实现方式,在这一小段代码中 还看到了另一个方法 value(),可以看到,这个方法就是判断是否是一个匿名方法,如果是就执行方法并返回,不是就直接返回。
public static function first($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
if (empty($array)) {
return value($default);
}
foreach ($array as $item) {
return $item;
}
}
foreach ($array as $key => $value) {
if (call_user_func($callback, $value, $key)) {
return $value;
}
}
return value($default);
}
if (! function_exists('value')) {
/**
* Return the default value of the given value.
*
* @param mixed $value
* @return mixed
*/
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
看到这里,几乎是比较完整的实现,在这里还想到了另外一个和助手函数有关的项目:Underscore.php
这是一个 PHP 的方法库,也可以算是是 JS中的 underscore.js
和 loadsh
、ramda
的 PHP 实现。
在这个项目中我们看到了实现方式
public static function find($array, Closure $closure)
{
foreach ($array as $key => $value) {
if ($closure($value, $key)) {
return $value;
}
}
return;
}
这里的实现更为简单,最终也实现了我们想要的结果。