检查一个数组的任何元素是否在另一个数组中

问题描述 投票:0回答:9

我在 PHP 中有两个数组,如下所示:

人们:

Array
(
    [0] => 3
    [1] => 20
)

通缉犯:

Array
(
    [0] => 2
    [1] => 4
    [2] => 8
    [3] => 11
    [4] => 12
    [5] => 13
    [6] => 14
    [7] => 15
    [8] => 16
    [9] => 17
    [10] => 18
    [11] => 19
    [12] => 20
)

如何检查People元素中的任何是否在通缉罪犯数组中?

在此示例中,它应该返回

true
,因为
20
位于 通缉犯中。

php arrays filtering
9个回答
280
投票

您可以使用

array_intersect()

$peopleContainsCriminal = !empty(array_intersect($people, $criminals));

33
投票

使用 array_intersect() 和 count() (而不是空)没有什么问题。

例如:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;

29
投票

该代码无效,因为您只能将变量传递到语言构造中。

empty()
是一种语言构造。

您必须分两行完成此操作:

$result = array_intersect($people, $criminals);
$result = !empty($result);

28
投票

如果“空”不是最好的选择,那怎么样:

if (array_intersect($people, $criminals)) {...} //when found

if (!array_intersect($people, $criminals)) {...} //when not found

22
投票

in_array 与 array_intersect 的性能测试:

$a1 = array(2,4,8,11,12,13,14,15,16,17,18,19,20);

$a2 = array(3,20);

$intersect_times = array();
$in_array_times = array();
for($j = 0; $j < 10; $j++)
{
    /***** TEST ONE array_intersect *******/
    $t = microtime(true);
    for($i = 0; $i < 100000; $i++)
    {
        $x = (bool) array_intersect($a1,$a2);
        // $x = array_intersect($a1,$a2);
        // $x = !empty($x);
    }
    $intersect_times[] = microtime(true) - $t;


    /***** TEST TWO in_array *******/
    $t2 = microtime(true);
    for($i = 0; $i < 100000; $i++)
    {
        $x = false;
        foreach($a2 as $v){
            if(in_array($v,$a1))
            {
                $x = true;
                break;
            }
        }
    }
    $in_array_times[] = microtime(true) - $t2;
}

echo '<hr><br>'.implode('<br>',$intersect_times).'<br>array_intersect avg: '.(array_sum($intersect_times) / count($intersect_times));
echo '<hr><br>'.implode('<br>',$in_array_times).'<br>in_array avg: '.(array_sum($in_array_times) / count($in_array_times));
exit;

这里是更新的结果,在旧的 Android 手机上使用 php 8.1.6 删除了“空”(感谢 @mickmackusa):

0.48460602760315
0.48387813568115
0.48307418823242
0.48364686965942
0.48398804664612
0.48381400108337
0.48366618156433
0.48358988761902
0.48365116119385
0.48321080207825
array_intersect avg: 0.48371253013611

0.023025989532471
0.022964954376221
0.023096084594727
0.022902965545654
0.022971868515015
0.022997856140137
0.02297306060791
0.022901058197021
0.022931814193726
0.023001909255981
in_array avg: 0.022976756095886

in_array 至少快了 5 倍,讽刺的是,尽管从 array_intersect 循环中删除了 empty() 函数,但在 php 8.1 中它看起来快了 20 倍。请注意,一旦找到结果,我们就会“中断”,但根据 $a1 和 $a2 中的测试值,它无论如何都是最后一个循环。


3
投票

您还可以按如下方式使用 in_array :

<?php
$found = null;
$people = array(3,20,2);
$criminals = array( 2, 4, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
foreach($people as $num) {
    if (in_array($num,$criminals)) {
        $found[$num] = true;
    } 
}
var_dump($found);
// array(2) { [20]=> bool(true)   [2]=> bool(true) }

虽然 array_intersect 使用起来确实更方便,但事实证明它在性能方面并不是真正优越。 我也创建了这个脚本:

<?php
$found = null;
$people = array(3,20,2);
$criminals = array( 2, 4, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
$fastfind = array_intersect($people,$criminals);
var_dump($fastfind);
// array(2) { [1]=> int(20)   [2]=> int(2) }

然后,我分别在 http://3v4l.org/WGhO7/perf#tabshttp://3v4l.org/g1Hnu/perf#tabs 运行这两个片段,并检查每个片段的性能。 有趣的是,PHP5.6 的总 CPU 时间(即用户时间 + 系统时间)是相同的,内存也是相同的。 PHP5.4 下 in_array 的总 CPU 时间比 array_intersect 少,尽管幅度不大。


2
投票

这是我研究了一段时间后的做法。我想制作一个 Laravel API 端点来检查字段是否“正在使用”,因此重要信息是:1)哪个数据库表? 2)什么数据库列? 3) 该列中是否有与搜索词匹配的值?

知道了这一点,我们就可以构造关联数组了:

$SEARCHABLE_TABLE_COLUMNS = [
    'users' => [ 'email' ],
];

然后,我们可以设置要检查的值:

$table = 'users';
$column = 'email';
$value = '[email protected]';

然后,我们可以相互使用

array_key_exists()
in_array()
来执行一、两步组合,然后根据
truthy
条件采取行动:

// step 1: check if 'users' exists as a key in `$SEARCHABLE_TABLE_COLUMNS`
if (array_key_exists($table, $SEARCHABLE_TABLE_COLUMNS)) {

    // step 2: check if 'email' is in the array: $SEARCHABLE_TABLE_COLUMNS[$table]
    if (in_array($column, $SEARCHABLE_TABLE_COLUMNS[$table])) {

        // if table and column are allowed, return Boolean if value already exists
        // this will either return the first matching record or null
        $exists = DB::table($table)->where($column, '=', $value)->first();

        if ($exists) return response()->json([ 'in_use' => true ], 200);
        return response()->json([ 'in_use' => false ], 200);
    }

    // if $column isn't in $SEARCHABLE_TABLE_COLUMNS[$table],
    // then we need to tell the user we can't proceed with their request
    return response()->json([ 'error' => 'Illegal column name: '.$column ], 400);
}

// if $table isn't a key in $SEARCHABLE_TABLE_COLUMNS,
// then we need to tell the user we can't proceed with their request
return response()->json([ 'error' => 'Illegal table name: '.$table ], 400);

我对 Laravel 特定的 PHP 代码表示歉意,但我将保留它,因为我认为您可以将其视为伪代码。 重要的部分是同步执行的两个

if
语句。

array_key_exists()
in_array()
是 PHP 函数。

来源:

我上面展示的算法的好处是,您可以创建一个 REST 端点,例如

GET /in-use/{table}/{column}/{value}
(其中
table
column
value
是变量)。

你可以:

$SEARCHABLE_TABLE_COLUMNS = [
    'accounts' => [ 'account_name', 'phone', 'business_email' ],
    'users' => [ 'email' ],
];

然后你可以发出 GET 请求,例如:

GET /in-use/accounts/account_name/Bob's Drywall
(您可能需要对最后一部分进行uri编码,但通常不需要)

GET /in-use/accounts/phone/888-555-1337

GET /in-use/users/email/[email protected]

还要注意,没有人可以做到:

GET /in-use/users/password/dogmeat1337
,因为
password
未列在
user
的允许列列表中。

祝您旅途顺利。


0
投票

从 PHP8.4 开始,

array_any()
提供了函数式方法,具有条件短路(提前返回)的性能优势。

该函数返回一个布尔值,即使找到匹配项,也无需遍历 Needle 数组的每个元素。

array_intersect()
无法承担这种奢侈——它无条件地迭代一切。

虽然

array_intersect()
(以及
_intersect
_diff()
函数的整个数组过滤“家族”)旨在对数据进行底层排序,以便比暴力
in_array()
调用有可能提高性能,但事实是这些函数没有能力提前返回。 这意味着即使第一次比较返回
true
评估,所有数组的整个有效负载都将被不必要地遍历。

代码:(演示

$a = [2,4,8,11,12,13,14,15,16,17,18,19,20];
$b = [3,14];

var_export(
    array_any($a, fn($v) => in_array($v, $b))
);

了解使用这种长手语法进行短路的好处。 演示

var_export(
    array_any(
        $a,
        function($v) use ($b) {
            echo "checking if $v in the \$b array\n";
            return in_array($v, $b);
        }
    )
);

输出:

checking if 2 in the $b array
checking if 4 in the $b array
checking if 8 in the $b array
checking if 11 in the $b array
checking if 12 in the $b array
checking if 13 in the $b array
checking if 14 in the $b array
true

PHP8.4 的

array_any()
是比在立即调用函数表达式内的可破坏 foreach 内编写条件返回更具可读性和优雅的函数脚本。

var_export(
    (function() use ($a, $b) {
        foreach ($a as $v) {
            echo "checking if $v in the \$b array\n";
            if (in_array($v, $b)) {
                return true;
            }
        }
        return false;
    })()
);

这是 嵌套

array_any()
调用的实现,作为
array_uintersect()
的替代。


-2
投票

我创建了一个干净的辅助函数供您使用。

if (!function_exists('array_has_one')) {

/**
 * array_has_one
 * 
 * Uses the search array to match at least one of the haystack to return TRUE
 * 
 * @param {array} $search
 * @param {array} $haystack
 * @return {boolean}
 */
function array_has_one(array $search, array $haystack){
    if(!count(array_intersect($search, $haystack)) === FALSE){
        return TRUE;
    }else{
        return FALSE;
    }

}
}

你会像这样使用它

if(array_has_one([1,2,3,4,5], [5,6,7,8,9])){
  echo "FOUND 5";
}
© www.soinside.com 2019 - 2024. All rights reserved.