【问题标题】:Checking to see if one array's elements are in another array in PHP检查一个数组的元素是否在 PHP 中的另一个数组中
【发布时间】:2010-10-06 03:29:27
【问题描述】:

我在 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


    【解决方案1】:

    您可以使用array_intersect()。

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

    【讨论】:

    • 不能将 empty() 与变量以外的任何东西一起使用。
    • @grantwparks 那么为什么在关于此函数的 PHP 文档中他们说“如果 var 存在并且具有非空、非零值,则返回 FALSE。否则返回 TRUE。以下内容被认为是空的: 数组()(一个空数组)”?来源:php.net/manual/en/function.empty.php
    • 从您链接到的页面:“在 PHP 5.5 之前,empty() 仅支持变量;其他任何内容都会导致解析错误。换句话说,以下内容将不起作用:empty(trim ($name))。改为使用 trim($name) == false。"
    • 如 cmets 中所述,我发现 !empty 无法按预期工作。相反,我使用了count():!count(array_intersect($people, $criminals));
    • 为什么当它抛出致命错误时被标记为 65 票的答案:不能在写入上下文中使用函数返回值?
    【解决方案2】:

    该代码无效,因为您只能将变量传递给语言结构。 empty() 是一种语言结构。

    您必须分两行完成:

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

    【讨论】:

    • 问题不在于它是一种语言结构。问题是它需要一个引用,而 Greg 正在传递一个值。
    • @Artefacto,来自 php.net “注意:因为这是一种语言结构而不是函数,所以不能使用变量函数调用它。”就像保罗说的那样。
    【解决方案3】:

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

    例如:

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

    【讨论】:

    • 没有什么问题,但count() 不被认为是高性能的(如果你关心微优化,那就是)
    • 这段代码中三元运算符是多余的,你可以直接把它解析为布尔值,在count前面调用(bool)
    • 可以做到$bFound = count(array_intersect($criminals, $people)) > 0;
    【解决方案4】:

    如果 'empty' 不是最好的选择,那么这个呢:

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

    或

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

    【讨论】:

      【解决方案5】:

      你也可以像下面这样使用 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#tabs 和http://3v4l.org/g1Hnu/perf#tabs 上运行了两个sn-ps,并检查了每个sn-ps 的性能。有趣的是,PHP5.6 的总 CPU 时间,即用户时间 + 系统时间是相同的,内存也是相同的。在 PHP5.4 下,in_array 的总 CPU 时间比 array_intersect 少,尽管有点少。

      【讨论】:

      • 结果是骗人的。只运行一次太快了,无法衡量任何差异。如果您每秒有成百上千个请求,那么这几分之一秒会很快加起来,所以如果您认为您的应用程序需要扩展,我会坚持使用 in_array 实现。
      【解决方案6】:

      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 = 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;
      

      结果如下:

      0.26520013809204
      0.15600109100342
      0.15599989891052
      0.15599989891052
      0.1560001373291
      0.1560001373291
      0.15599989891052
      0.15599989891052
      0.15599989891052
      0.1560001373291
      array_intersect avg: 0.16692011356354
      
      0.015599966049194
      0.031199932098389
      0.031200170516968
      0.031199932098389
      0.031200885772705
      0.031199932098389
      0.031200170516968
      0.031201124191284
      0.031199932098389
      0.031199932098389
      in_array avg: 0.029640197753906
      

      in_array 至少快 5 倍。请注意,一旦找到结果,我们就会“中断”。

      【讨论】:

      • 感谢基准测试。因此,如果您知道自己正在处理小型数组,最好还是使用array_intersect()。
      • isset 甚至更快。您可以使用 bool val 来启用或禁用。此外,作为键的搜索值确保没有重复项。 'array_intersect avg: 0.52077736854553; in_array 平均值:0.015597295761108; isset 平均:0.0077081203460693´
      • 如果你使用 in_array 和严格的布尔集,它会更快。测试平均值比没有严格的布尔集快 3 倍。
      【解决方案7】:

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

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

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

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

      $table = 'users';
      $column = 'email';
      $value = 'alice@bob.com';
      

      然后,我们可以使用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/alice@bob.com

      还要注意没有人能做到:

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

      祝你旅途愉快。

      【讨论】:

      • 我不知道这与问题有什么关系,但我看了看:我真的希望你永远不要在$SEARCHABLE_TABLE_COLUMNS 中使用动态数据!这尖叫着注入 - 无论是否存在试图屏蔽和过滤表和列字符串的“超安全框架查询构建器”!最后表和列字符串不能通过占位符(准备好的语句)添加,必须像SELECT ... FROM {$table} WHERE {$column} = :placeholder .... 一样直接插入。 Ofc 取决于适配器(mysql,mongo,...),但这不是要保存的参数!请静态或无列表 =)
      猜你喜欢
      • 2021-01-30
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      • 2020-06-13
      相关资源
      最近更新 更多