PHP: Überprüfen Sie, ob ein Array Duplikate enthält

71

Ich bin sicher, dass dies eine äußerst offensichtliche Frage ist und dass es eine Funktion gibt, die genau dies tut, aber ich kann sie scheinbar nicht finden. In PHP möchte ich wissen, ob mein Array so effizient wie möglich Duplikate enthält. Ich möchte sie nicht wie bisher entfernen array_unique, und ich möchte sie nicht besonders ausführen array_uniqueund mit dem ursprünglichen Array vergleichen, um festzustellen, ob sie gleich sind, da dies sehr ineffizient erscheint. In Bezug auf die Leistung besteht die "erwartete Bedingung" darin, dass das Array keine Duplikate enthält.

Ich möchte nur in der Lage sein, so etwas zu tun

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

Gibt es eine offensichtliche Funktion, an die ich nicht denke?
Wie erkenne ich doppelte Werte im PHP-Array?
hat den richtigen Titel und ist eine sehr ähnliche Frage. Wenn Sie die Frage jedoch tatsächlich lesen, sucht er nach array_count_values.

Mala
quelle
Möchten Sie nur wissen, ob es Duplikate gibt oder wie viele und wie viele Duplikate usw. vorhanden sind?
1
Ich muss nur wissen, ob es Duplikate gibt. Die Rückgabe eines Booleschen Werts ist perfekt.
Mala
20
Ehrlich gesagt denke ich, if(count($array) == count(array_unique($array)))ist das Beste, was Sie bekommen können. Sie müssen das Array auf die eine oder andere Weise durchlaufen, und ich denke, die eingebauten sind dafür optimiert. array_flipkönnte auch in Betracht gezogen werden.
Felix Kling
@ Felix, du kannst es besser machen. Das macht drei Schleifen, eine zum Erstellen des eindeutigen Arrays, eine zum Zählen und eine zum Zählen des Originals.
Mike Sherov
@ Mike Sherov: Bist du sicher? Ich konnte nichts darüber finden, aber ich hatte gehofft, dass PHP-Arrays eine interne Eigenschaft haben, die die Länge verfolgt. Haben Sie Informationen dazu? Das würde mich sehr interessieren.
Felix Kling

Antworten:

40

Du kannst tun:

function has_dupes($array) {
    $dupe_array = array();
    foreach ($array as $val) {
        if (++$dupe_array[$val] > 1) {
            return true;
        }
    }
    return false;
}
Mike Sherov
quelle
1
Es ist implizit definiert, aber ich werde die Antwort aus Gründen der Klarheit bearbeiten.
Mike Sherov
6
Ich mag das! Denken Sie daran, dass dies auch bei einem frühen returnZeitpunkt eine O (n) -Funktion ist. Zusätzlich zum Overhead foreachund Tracking $dupe_arraywürde ich gerne ein Benchmarking sehen. Ich würde vermuten, dass für Arrays ohne Duplikate die Verwendung nativer Funktionen schneller ist. Auf jeden Fall besser als O (n ^ 2). Nett.
Jason McCreary
2
Hat ein kleines Problem: Funktioniert nur richtig, wenn die Werte Zeichenfolgen oder Zahlen sind.
Artefacto
10
Dieser Code gab mir einen undefined offsetFehler in PHP. Stattdessen habe ich:foreach ( $a as $v ) { if ( array_key_exists($v,$dupe) { return true; } else { $dupe[$v] = true; }
EleventyOne
3
Wie funktioniert das überhaupt? Da $dupe_arraymit keinem Wert definiert wurde, $dupe_array[$val]sollte ein undefinierter Index zurückgegeben werden!
Nikunj Madhogaria
221

Ich weiß, dass du nicht danach bist array_unique(). Sie werden jedoch weder eine magische offensichtliche Funktion finden, noch wird das Schreiben schneller sein als die Verwendung der nativen Funktionen.

Ich schlage vor:

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

Passen Sie den zweiten Parameter von array_unique()an Ihre Vergleichsanforderungen an.

Jason McCreary
quelle
3
danke für den Vorschlag. Mein Gedanke bei der Suche nach einem besseren Algorithmus ist einfach, dass Sie technisch gesehen array_uniquewissen sollten, ob Dupes vorhanden sind, wenn Sie mit dem Ausführen der integrierten Funktionen fertig sind . Alles, was mindestens so viel Arbeit array_uniqueleistet wie mehr als nötig. Obwohl ja, wenn eine solche Funktion nicht existiert, habe ich keine besondere Lust, sie zu schreiben.
Mala
1
Wenn Sie sich nur darum kümmern, ob es Dupes gibt, dann würde ich das tun. Wenn Sie sich mehr als nur darum kümmern, ob es Dupes gibt, dann haben Sie Recht, das oben Genannte kann mehr Arbeit leisten, als es braucht. Alles, was Sie schreiben, wird O (n ^ 2) sein. Auch wenn Sie früh aussteigen. Wie Sie sagten, ist es nicht üblich, dass Sie Dupes haben. Lohnt es sich also, etwas Magisches zu machen?
Jason McCreary
Magisch? Sicher, es ist eine Mikrooptimierung, aber es ist keine "Magie", eine eigene Funktion zu schreiben, und ich bin mir nicht sicher, ob eine bessere Lösung so viel schwieriger zu schreiben ist als diese.
Mike Sherov
1
Ich bin nur hierher gekommen, um genau diese Antwort zu finden :)
Gino Pane
4
Elegant, aber array_uniqueetwas langsam. Wenn Sie wissen, dass das Array nur Ganzzahlen und Zeichenfolgen enthält, können Sie es durch ersetzen, array_flipum schnellere Ergebnisse zu erzielen.
Tgr
76

⚡ LEISTUNGSLÖSUNG ⚡

Wenn Sie sich für Leistung und Mikrooptimierungen interessieren, überprüfen Sie diesen Einzeiler:

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

Beschreibung:

Die Funktion vergleicht die Anzahl der Array-Elemente $input_arraymit den Elementen von array_flip . Werte werden zu Schlüsseln und raten Sie mal - Schlüssel müssen in assoziativen Arrays eindeutig sein, damit keine eindeutigen Werte verloren gehen und die endgültige Anzahl der Elemente niedriger als das Original ist.

Wie gesagt in manuellen Array - Schlüssel kann nur der Typ sein , intoder stringso ist es das , was Sie in Original - Array Werte haben zu vergleichen, sonst wird PHP beginnen Gießen mit unerwarteten Ergebnissen.

Beweis für 10M RECORDS ARRAY

  • Am häufigsten gewählte Lösung: 14.187316179276s 🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌
  • Akzeptierte Lösung: 2.0736091136932s 🐌🐌
  • Diese Antwortlösung: 0.14155888557434s 🐌 / 10

Testfall:

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

Beachten Sie, dass die akzeptierte Lösung unter bestimmten Umständen schneller sein kann, wenn sich nicht eindeutige Werte am Anfang eines großen Arrays befinden.

s3m3n
quelle
Funktioniert es nur, wenn Array-Werte keine Objekte sind?
Oleg Abrazhaev
Ja, das ist richtig. Es wurde ein Absatz hinzugefügt, um zu verdeutlichen, dass Array-Schlüssel nur intoder stringso sein können. Dies müssen Ihre Werte im Array sein, um sie zu vergleichen.
s3m3n
@ Mickmackusa wie in der Antwort gesagt : 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
Genau das, wonach ich gesucht habe. Vielen Dank!
DiMono
1
@ErdalG. Dies ist schneller, da array_flipdie native PHP-Funktion in C geschrieben ist und das Umdrehen ziemlich einfach ist. Nach dem Umdrehen werden nicht eindeutige Werte entfernt, da dies zu einem Array-Schlüsselkonflikt führen kann.
s3m3n
15
$hasDuplicates = count($array) > count(array_unique($array)); 

Wird sein, truewenn Duplikate oder falsewenn keine Duplikate.

Andrew
quelle
Witzige und elegante Lösung.
Marcos Labad
6
$duplicate = false;

 if(count(array) != count(array_unique(array))){
   $duplicate = true;
}
Breite deine Flügel aus
quelle
4

Hier ist meine Meinung dazu… Nach einigem Benchmarking fand ich, dass dies die schnellste Methode dafür ist.

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

… Oder je nach Umständen könnte dies geringfügig schneller sein.

function has_duplicates( $array ) {
    $array = array_count_values( $array );
    rsort( $array );
    return $array[0] > 1;
}
Micadelli
quelle
2
Nicht sicher, warum Sie array_keys()in Ihrer Antwort brauchen . array_flip()Verdichtet Ihr Array bereits, wenn die Werte gleich sind. Auch !=ist ein ausreichender Vergleich, da die Typen von Natur aus gleich sind count()(Sie sind derjenige, der Benchmarking erwähnt hat). Daher return count(array_flip($arr)) != count($arr);sollte ausreichend sein.
Cartbeforehorse
2

Halte es einfach, dumm! ;)

Einfache ODER-Logik ...

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

Grüße!

Miles Bennet
quelle
2
#BadCode - Die besten Möglichkeiten, diese Prüfung mit Funktionen von PHP selbst durchzuführen.
FabianoLothor
0

Zwei Möglichkeiten, es effizient zu machen, die ich mir vorstellen kann:

  1. Einfügen aller Werte in eine Hashtabelle und Überprüfen, ob der Wert, den Sie einfügen, bereits darin enthalten ist (erwartete O (n) Zeit und O (n) Raum)

  2. Sortieren des Arrays und anschließendes Überprüfen, ob benachbarte Zellen je nach Sortieralgorithmus gleich sind (O (nlogn) -Zeit und O (1) - oder O (n) -Raum)

Die Lösung von Stormdrain wäre wahrscheinlich O (n ^ 2), ebenso wie jede Lösung, bei der das Array nach jedem Element durchsucht wird, das nach einem Duplikat sucht

Bwmat
quelle
0

Finden Sie diese nützliche Lösung

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

Nach dieser Zählung Ergebnis, wenn größer als 0 als Duplikate sonst eindeutig.

Muhammad Raheel
quelle
0

Ich benutze dies:

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

Ich weiß nicht, ob es das schnellste ist, aber es funktioniert bisher ziemlich gut

Abraham Romero
quelle
0
function hasDuplicate($array){
  $d = array();
  foreach($array as $elements) {
    if(!isset($d[$elements])){
      $d[$elements] = 1;
    }else{
      return true;
    } 
  } 
  return false;
}
Alo
quelle
1
Wo ist die Erklärung, was dieser Quellcode leisten wird?
J. Murray
-1

Wie Sie ausdrücklich sagten, dass Sie nicht verwenden wollten, array_uniquewerde ich die anderen Antworten ignorieren, obwohl sie wahrscheinlich besser sind.

Warum verwenden Sie nicht array_count_values ​​() und prüfen dann, ob das resultierende Array einen Wert größer als 1 hat?


quelle
-1

Sie können es auch so machen: Dies gibt true zurück, wenn unique else false zurückgibt.

$nofollow = (count($modelIdArr) !== count(array_unique($modelIdArr))) ? true : false;
Lakhan
quelle
-1

Die einfache Lösung aber ganz schneller.

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

Geschwindigkeit - [0,71]!

Юрий Светлов
quelle