【问题标题】:php: check if an array has duplicatesphp:检查数组是否有重复项
【发布时间】:2011-03-09 21:43:24
【问题描述】:

我确信这是一个非常明显的问题,并且有一个函数可以做到这一点,但我似乎找不到它。在 PHP 中,我想尽可能高效地知道我的数组中是否有重复项。我不想像array_unique 那样删除它们,我也不想特别想运行array_unique 并将其与原始数组进行比较以查看它们是否相同,因为这似乎非常低效。就性能而言,“预期条件”是数组没有重复。

我只是希望能够做类似的事情

if (no_dupes($array))
    // this deals with arrays without duplicates
else
    // this deals with arrays with duplicates

有什么我没有想到的明显功能吗?
How to detect duplicate values in PHP array?
有正确的标题,并且是一个非常相似的问题,但是如果你真的阅读了这个问题,他正在寻找 array_count_values。

【问题讨论】:

  • 您是否只想知道是否有重复或重复的数量和价值等?
  • 我只需要知道是否有任何重复。返回一个布尔值是完美的。
  • 老实说,我认为if(count($array) == count(array_unique($array))) 是你能得到的最好的。您必须以这种或另一种方式遍历数组,我认为内置为此进行了优化。 array_flip 也可以考虑。
  • @Felix,你可以做得更好。这会执行三个循环,一个用于创建唯一数组,一个用于计算它,一个用于计算原始数组。
  • @Mike Sherov:你确定吗?我找不到任何关于它的信息,但我希望 PHP 数组有一些内部属性来跟踪长度。你有这方面的信息吗?我会很感兴趣的。

标签: php arrays duplicates


【解决方案1】:

我知道你不在array_unique()之后。但是,您不会发现 magical obvious 函数,编写一个函数也不会比使用原生函数更快。

我建议:

function array_has_dupes($array) {
   // streamline per @Felix
   return count($array) !== count(array_unique($array));
}

调整array_unique()的第二个参数,满足你的对比需求。

【讨论】:

  • 感谢您的建议。我寻找更好算法的想法很简单,从技术上讲,一旦你完成了内置 array_unique 所做的任何事情,你应该能够知道是否有任何欺骗。因此,任何工作至少与array_unique 一样多的工作都比必要的工作多。虽然是啊,如果没有这样的函数,我也不是特别想写。
  • 如果你只关心它是否有骗子,那我会这样做。如果你关心的不仅仅是它是否有欺骗,那么你是对的,上面的工作可能比它需要的要多。你写的任何东西都将是 O(n^2)。即使你提早出手。正如你所说,你有被骗的情况并不常见。那么,花时间创造一些神奇的东西值得吗?
  • 神奇?当然这是一个微优化,但编写自己的函数并不是“魔法”,我不确定是不是更好的解决方案比这更难编写。
  • 我来到这里只是为了找到这个答案:)
  • 优雅,但array_unique 有点慢。如果您知道数组只包含整数和字符串,则可以将其替换为 array_flip 以获得更快的结果。
【解决方案2】:

⚡性能解决方案⚡

如果您关心性能和微优化,请查看以下单行代码:

function no_dupes(array $input_array) {
    return count($input_array) === count(array_flip($input_array));
}

说明:

函数将$input_array 中的数组元素数与array_flip'ed 元素进行比较。值成为键并猜测 - 键在关联数组中必须是唯一的,因此不会丢失唯一值并且最终元素数量低于原始数量。

正如manual 中所说,数组键只能是intstring 的类型,所以这是您可以在原始数组值中比较的类型,否则 PHP 将以意外结果启动 casting

10M 记录阵列的证明

  • 投票最多的解决方案:14.187316179276s ??????????????
  • 接受的解决方案:2.0736091136932s ??
  • 这个答案解决方案:0.14155888557434s ?/10

测试用例:

<?php

$elements = array_merge(range(1,10000000),[1]);

$time = microtime(true);
accepted_solution($elements);
echo 'Accepted solution: ', (microtime(true) - $time), 's', PHP_EOL;

$time = microtime(true);
most_voted_solution($elements);
echo 'Most voted solution: ', (microtime(true) - $time), 's', PHP_EOL;

$time = microtime(true);
this_answer_solution($elements);
echo 'This answer solution: ', (microtime(true) - $time), 's', PHP_EOL;

function accepted_solution($array){
 $dupe_array = array();
 foreach($array as $val){
  // sorry, but I had to add below line to remove millions of notices
  if(!isset($dupe_array[$val])){$dupe_array[$val]=0;}
  if(++$dupe_array[$val] > 1){
   return true;
  }
 }
 return false;
}

function most_voted_solution($array) {
   return count($array) !== count(array_unique($array));
}

function this_answer_solution(array $input_array) {
    return count($input_array) === count(array_flip($input_array));
}

请注意,当非唯一值靠近巨大数组的开头时,在某些情况下,可接受的解决方案可能会更快。

【讨论】:

  • 只有在数组值不是对象的情况下才有效吗?
  • 是的,没错。添加段落以使其明显,数组键只能是 intstring 所以这必须是您要比较的数组中的值。
  • @ErdalG。这更快,因为array_flipnative PHP function written in C 并且翻转是非常简单的操作。翻转后不唯一的值会被删除,因为它们可能会造成数组键冲突。
  • 我之前肯定提到过并删除了我的评论,但是我认为这个“优化”答案的最响亮部分应该是存在边缘情况的事实这将导致这个自定义函数失败(而不是引人注目的闪电和蜗牛)。 3v4l.org/7bRXI
  • @mickmackusa 在描述中有第二段,您只能将其与 intstring 数组键一起使用。此信息在蜗牛之前注明。抱歉,如果该方法引起任何问题。
【解决方案3】:

你可以这样做:

function has_dupes($array) {
    $dupe_array = array();
    foreach ($array as $val) {
        if (++$dupe_array[$val] > 1) {
            return true;
        }
    }
    return false;
}

【讨论】:

  • 它是隐式定义的,但为了清楚起见,我会编辑答案。
  • 我喜欢!请记住,即使是早期的return,这也是一个 O(n) 函数。除了foreach 和跟踪$dupe_array 的开销之外,我还希望看到一些基准测试。我猜对于没有重复的数组,使用本机函数会更快。不过绝对比 O(n^2) 好。不错。
  • 有个小问题:只有当值是字符串或数字时才能正常工作。
  • 这段代码在 PHP 中给了我一个undefined offset 错误。相反,我做到了:foreach ( $a as $v ) { if ( array_key_exists($v,$dupe) { return true; } else { $dupe[$v] = true; }
  • 这是如何工作的?由于$dupe_array 没有定义任何值,$dupe_array[$val] 应该返回一个未定义的索引!
【解决方案4】:
$hasDuplicates = count($array) > count(array_unique($array)); 

如果重复则为true,如果没有重复则为false

【讨论】:

【解决方案5】:
$duplicate = false;

 if(count(array) != count(array_unique(array))){
   $duplicate = true;
}

【讨论】:

【解决方案6】:

这是我对此的看法……经过一些基准测试后,我发现这是最快的方法。

function has_duplicates( $array ) {
    return count( array_keys( array_flip( $array ) ) ) !== count( $array );
}

…或者根据情况,这可能会稍微快一些。

function has_duplicates( $array ) {
    $array = array_count_values( $array );
    rsort( $array );
    return $array[0] > 1;
}

【讨论】:

  • 不知道为什么你的答案需要array_keys()。如果值相同,array_flip() 已经压缩了您的数组。此外,!= 是一个足够的比较器,因为count() 的类型本质上是相同的(你是提到基准测试的那个)。因此return count(array_flip($arr)) != count($arr); 应该足够了。
  • 此答案中的技术与@s3m3n 的函数具有相同的漏洞。 3v4l.org/3FlBJ 这是一个“apples-vs-oranges”比较,所以我认为任何基准比较都是不合适的,因为该函数不提供相同的行为。
【解决方案7】:

保持简单,傻! ;)

简单的 OR 逻辑...

function checkDuplicatesInArray($array){
    $duplicates=FALSE;
    foreach($array as $k=>$i){
        if(!isset($value_{$i})){
            $value_{$i}=TRUE;
        }
        else{
            $duplicates|=TRUE;          
        }
    }
    return ($duplicates);
}

问候!

【讨论】:

  • #BadCode - 使用 PHP 本身的函数进行此检查的最佳方法。
  • 我发现可变变量通常没有吸引力的解决方案。这种技术在某些情况下可能会失败。 3v4l.org/kGLWTMoreso,PHP7.4 及以上版本。
【解决方案8】:

要从比较中删除所有空值,您可以添加 array_diff()

if (count(array_unique(array_diff($array,array("")))) < count(array_diff($array,array(""))))

参考来自@AndreKR 来自here的回答

【讨论】:

    【解决方案9】:

    我能想到的两种高效的方法:

    1. 将所有值插入某种哈希表并检查您要插入的值是否已经在其中(预计 O(n) 时间和 O(n) 空间)

    2. 对数组进行排序,然后检查相邻单元格是否相等(O(nlogn) 时间和 O(1) 或 O(n) 空间,具体取决于排序算法)

    stormdrain 的解决方案可能是 O(n^2),任何涉及扫描数组以查找重复元素的解决方案也是如此

    【讨论】:

      【解决方案10】:

      找到这个有用的解决方案

      function get_duplicates( $array ) {
          return array_unique( array_diff_assoc( $array, array_unique( $array ) ) );
      }
      

      如果大于 0 则计数结果大于重复,否则为唯一。

      【讨论】:

      • 尽管是单线技术,但这种技术似乎比其他发布的答案进行了更多的处理。要在不调用 count() 的情况下检查数组是否为空,只需使用 ! 进行错误检查:3v4l.org/O4g3F
      【解决方案11】:

      我正在使用这个:

      if(count($array)==count(array_count_values($array))){
          echo("all values are unique");
      }else{
          echo("there's dupe values");
      }
      

      我不知道它是否是最快的,但到目前为止效果还不错

      【讨论】:

      • 某些数据类型会导致此技术失败,因此这不是一个可靠/稳健的解决方案。 3v4l.org/FSr7P
      【解决方案12】:

      我的另一个解决方案,这与性能提升有关

      $array_count_values = array_count_values($array);
      if(is_array($array_count_values) && count($array_count_values)>0)
      {
         foreach ($array_count_values as $key => $value)
         {
            if($value>1)
            {
              // duplicate values found here, write code to handle duplicate values            
            }
         }
      }
      

      【讨论】:

        【解决方案13】:

        正如您特别说过的,您不想使用 array_unique,尽管事实上它们可能更好,但我将忽略其他答案。

        你为什么不使用array_count_values(),然后检查结果数组是否有任何大于1的值?

        【讨论】:

          【解决方案14】:

          php 有一个函数可以计算数组http://www.php.net/manual/en/function.array-count-values.php 中出现的次数

          【讨论】:

            【解决方案15】:

            你也可以这样做: 如果 unique 则返回 true,否则返回 false。

            $nofollow = (count($modelIdArr) !== count(array_unique($modelIdArr))) ? true : false;
            

            【讨论】:

            【解决方案16】:

            简单的解决方案,但速度相当快。

            $elements = array_merge(range(1,10000000),[1]);
            
            function unique_val_inArray($arr) {
                $count = count($arr);
                foreach ($arr as $i_1 => $value) {
                    for($i_2 = $i_1 + 1; $i_2 < $count; $i_2++) {
                        if($arr[$i_2] === $arr[$i_1]){
                            return false;
                        }
                    }
                }
                return true;
            }
            
            $time = microtime(true);
            unique_val_inArray($elements);
            echo 'This solution: ', (microtime(true) - $time), 's', PHP_EOL;
            

            速度 - [0.71]!

            【讨论】:

              【解决方案17】:
              function hasDuplicate($array){
                $d = array();
                foreach($array as $elements) {
                  if(!isset($d[$elements])){
                    $d[$elements] = 1;
                  }else{
                    return true;
                  } 
                } 
                return false;
              }
              

              【讨论】:

              • 这个源代码将完成什么的解释在哪里?
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-04-23
              • 1970-01-01
              • 1970-01-01
              • 2017-12-09
              相关资源
              最近更新 更多