php:检查数组是否重复


72

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

我只想做类似的事情

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

我有没有想到的明显功能?
如何检测PHP数组中的重复值?
具有正确的标题,并且是一个非常相似的问题,但是,如果您实际阅读了该问题,他正在寻找array_count_values。


您是否只想知道是否有重复项或这些重复项的数量和价值等?

1
我只需要知道是否有重复项。返回布尔值是完美的。
马拉

20
老实说,我认为这if(count($array) == count(array_unique($array)))是最好的。您必须以这种方式遍历数组,我认为内置函数对此进行了优化。array_flip也可以考虑。
Felix Kling 2010年

@Felix,您可以做得更好。这执行了三个循环,一个循环创建唯一的数组,一个循环对其进行计数,以及一个循环对原始数组进行计数。
Mike Sherov 2010年

@Mike Sherov:确定吗?我找不到任何有关它的信息,但我希望PHP数组具有一些可跟踪长度的内部属性。你有这方面的信息吗?我会很感兴趣。
Felix Kling

Answers:


39

你可以做:

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

1
它是隐式定义的,但是为了清楚起见,我将对其进行编辑。
Mike Sherov 2010年

7
我喜欢!请记住,即使是很早,return这也是一个O(n)函数。除了foreach跟踪的开销外$dupe_array,我还希望看到一些基准测试。我猜没有重复的数组,利用本机函数更快。绝对比O(n ^ 2)好。真好
杰森·麦克雷里

2
有一个小问题:仅当值是字符串或数字时才能正常工作。
Artefacto 2010年

10
这段代码给了我一个undefined offsetPHP错误。相反,我做了:foreach ( $a as $v ) { if ( array_key_exists($v,$dupe) { return true; } else { $dupe[$v] = true; }
EleventyOne 2013年

3
这怎么工作?由于$dupe_array尚未定义任何值,因此$dupe_array[$val]应返回未定义的索引!
Nikunj Madhogaria

220

我知道你不追array_unique()。但是,您不会找到神奇的 显而易见的功能,也不会比使用本机功能更快地编写一个功能。

我提议:

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

调整的第二个参数array_unique()以满足您的比较需求。


3
谢谢你的建议。我想找到一个更好的算法的想法只是,从技术上讲,一旦完成了内置array_unique函数的运行,您就应该能够知道是否存在任何重复。因此,任何要做的事至少要array_unique多于必要的工作。尽管是的,但是,如果不存在这样的功能,我不会特别想编写它。
马拉

1
如果您只关心它是否有欺骗,那就是我会做的。如果您不仅仅在乎它是否有欺骗,那么您是对的,以上内容可能会做更多的工作。您写的任何东西都将是O(n ^ 2)。即使您提早纾困。正如您所说,骗子并不常见。那么,值得您花时间做些神奇的事情吗?
杰森·麦克雷里

神奇?当然,这是一个微优化,但是编写您自己的函数并不是“魔术”,而且我不确定这是一个更好的解决方案要比这难得多。
Mike Sherov 2010年

1
我来到这里只是为了找到确切的答案:)
吉诺·潘妮

4
优雅,但是array_unique有点慢。如果您知道数组仅包含整数和字符串,则可以将其替换array_flip为更快的结果。
Tgr 2015年

76

⚡性能解决方案⚡

如果您关心性能和微优化,请检查以下一线:

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

描述:

函数将数组元素$input_arrayarray_flip中的元素进行比较。值变成键并猜测-键在关联数组中必须是唯一的,因此不会丢失唯一值,并且元素的最终数量要少于原始值。

手动数组键中所述,只能是int或的类型,string这就是原始数组值中可以比较的类型,否则PHP将开始强制转换并产生意外结果。

1000万记录阵列的证明

  • 投票最多的解决方案: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));
}

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


它仅在数组值不是对象的情况下才起作用吗?
Oleg Abrazhaev,

是的,这是对的。为了使段落更明显,增加了一段,数组键只能是intstring所以这必须是您要比较的数组中的值。
s3m3n

@mickmackusa作为回答:As said in manual array keys can be only type of int or string so this is what you can have in original array values to compare, otherwise PHP will start casting with unexpected results.
s3m3n

您能解释一下为什么这样更快吗?同样,这也相反。因此,要进行公平的比较,您应该测试:function most_voted_solution($array) { return count($array) === count(array_unique($array)); }
Erdal G.

1
@ErdalG。这是更快的,因为array_flip用C编写的本机PHP函数,而flip是非常简单的操作。翻转后,不会删除唯一值,因为它们可能会导致数组键冲突。
s3m3n



4

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

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

2
不知道为什么需要array_keys()答案。array_flip()如果值相同,则已经压缩了数组。另外,它!=是一个足够的比较器,因为类型在本质上是相同的count()(您提到的是基准测试)。因此return count(array_flip($arr)) != count($arr);应该足够了。
cartbeforehorse 2014年

此答案中的技术与@ s3m3n函数具有相同的漏洞。 3v4l.org/3FlBJ 这是一个“ apples-vs-oranges”比较,因此,我认为任何基准比较都不适当,因为该函数不能提供相同的行为。
mickmackusa

2

保持简单,愚蠢!;)

简单或逻辑...

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

问候!


2
#BadCode-使用PHP本身的功能执行此检查的最佳方法。
FabianoLothor 2012年

我发现变量通常是没有吸引力的解决方案。在某些情况下,此技术可能会失败。3v4l.org/kGLWT Moreso,从PHP7.4起。
mickmackusa

0

我可以想到两种有效的方法:

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

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

暴雨的解决方案可能是O(n ^ 2),任何涉及扫描数组中每个元素以查找重复项的解决方案


0

找到这个有用的解决方案

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

在该计数结果之后,如果大于0则等于重复,否则唯一。


尽管是单行代码,但该技术似乎比其他已发布的答案进行了更多的处理。要检查数组是否为空而不调用count(),只需使用!以下命令
mickmackusa

0

我正在使用这个:

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

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


某些数据类型将导致此技术失败,因此这不是可靠/可靠的解决方案。3v4l.org/FSr7P
mickmackusa

-1

正如您明确地说的那样,您不想使用 array_unique尽管它们可能更好,但我将忽略其他答案。

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



-1

您也可以像这样进行操作:如果唯一,否则返回false,则返回true。

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

这几乎是@JasonMcCreary答案的重读。stackoverflow.com/a/3145647/2943403 我已投票删除此帖子。
mickmackusa

-1

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

$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]!


-1
function hasDuplicate($array){
  $d = array();
  foreach($array as $elements) {
    if(!isset($d[$elements])){
      $d[$elements] = 1;
    }else{
      return true;
    } 
  } 
  return false;
}

1
该源代码将完成什么的解释在哪里?
J. Murray,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.