PHP 2 Arrays vergleichen, verschiedene Längen

smallwall

Lt. Junior Grade
Registriert
Feb. 2014
Beiträge
446
Hi, wie überprüft man am besten 2 Arrays auf gleiche Werte? Die Werte müssen unter dem gleichen Schlüssel abgelegt sein (bzw. unter Schlüssel+1).

Hier ein Beispiel
PHP:
array(2) {
  [1]=>
  string(2) "e4"
  [2]=>
  string(2) "d5"
}
2.:
PHP:
array(243) {
    [0]=>
    string(2) "e4"
    [1]=>
    string(2) "c5"
    [2]=>
    string(3) "Nf3"
    [3]=>
    string(2) "d6"
    [4]=>
    string(2) "d4"
    [5]=>...

In diesem Fall liegt keine Übereinstimmung vor, deshalb soll nichts geschehen, bei einer Übereinstimmung soll das Array auf einen Stack geschoben werden (das ist ja kein Problem). Das Problem ist, dass das erste Array nicht immer nur 2 Zeilen hat, sondern möglicherweise auch 10 oder 20.

Wie mach ich das am geschicktesten? Meine alte Lösung war, dass ich viele if Abfragen gebaut habe, das gefällt mir aber so gar nicht mehr.

mfg
 
smallwall schrieb:
Hi, wie überprüft man am besten 2 Arrays auf gleiche Werte? Die Werte müssen unter dem gleichen Schlüssel abgelegt sein (bzw. unter Schlüssel+1).

ungefähr so (pseudo):
Code:
array_diff(array_values(array1), array_values(array2))
 
Vergleicht array1 mit einem oder mehr anderen Arrays und gibt die Werte aus array1 zurück, die in keinem der anderen Arrays enthalten sind.
Halte ich für unbrauchbar. Es müssen auch immer gleiche Schlüssel verwendet werden, Array1[0] == Array2[0] -> true, Array1[0] == Array2[2]->false
 
smallwall schrieb:
Halte ich für unbrauchbar. Es müssen auch immer gleiche Schlüssel verwendet werden, Array1[0]==Array2[0] -> true, Array1[0]==Array2[2]->false

Das geht schon, wenn die Indices/Schlüssel sukzessive sind. Dass es anders sein könnte, hast Du nicht angedeutet.
 
Du musst das genauer spezifizieren was du willst, ich werd daraus nicht schlau was du machen willst und was du finden willst.
(Ausser dass ich seh, dass es Schach ist :D)

falls ich es verstehe, kommt es mir stumpfsinnig vor, aber:
php funktion .. rekursiv z.b. oder per schleife(iterativ) .. nimmst die mit den weniger elementen.. ist ja immer gleicher indizie für beide arrays.. ist das array mit den weniger elementen durch und nichts gefunden -> false.

wenn die indizies nicht sukzessive sind, dann musst du wie bei bubble sort vorgehen^^ 1=1.n 2=1.n 3=1.n … oder du sortierst die arrays halt davor.
 
Zuletzt bearbeitet:
Hi

Was spricht gegen eine For-Schleife?

Bin in PHP-Syntax nicht so bewandert, aber müsste ungefähr so aussehen:
Code:
$identisch = true; //Annahme: Arrays sind gleich
for ($i=0; $i<$sizeArrayA && $identisch; $i++){
  $indentisch = (strcmp($ArrayA[$i], $ArrayB[$i]) == 0)
}
if($identisch){
  //Array auf Stack ... oder was auch immer
}

Wobei:
- ArrayA der kleinere Array sein muss - sonst muss man halt die Länge zuerst prüfen.
- Am Schluss musst den Wahrheitswert vom Boolean identisch prüfen.

strcmp: http://php.net/manual/de/function.strcmp.php

Ich bin aber der Meinung, dass dies Programmiertechnisch absolute Basiskenntnisse sind ...

Gruss - jumpin

PS: Bei deinem Beispiel fehlt im ersten Array das erste Element (also [0])

Edit: Code von C-Ähnlicher-Syntax auf PHP-ähnliche-Syntax angepasst ;)
 
Zuletzt bearbeitet:
PHP:
protected function compareArrays($array1, $array2) {
foreach($array1 as $key => $value) {
  if(isset($array2[$key]) && $array2[$key] != $value) {
    return false;
    break;
  }
}
return true;
}


Ungefähr so? Ich gehe mal davon aus: -Schlüssel/Werte beider Arrays müssen gleich sein, damit true, ist array1 Teilmenge von array2 (oder umgekehrt) auch true.

Das ist ne lineare Suche, solange deine Arrays nicht völlig eskalieren gibts IMHO keinen Grund das nicht über eine Stinoschleife zu regeln. Was heißt denn das 'bzw unter Schlüssel+1' aus deiner Frage? Wenn array1[1] = array2[2] dann auch true? Würde auch über den Ansatz gehen, dann halt mit leicht angepasster If-Abfrage.
 
Ja, Schach ist schon richtig. Ich habe ein Array mit der Eröffnung, und dann habe ich im 2. Array die Partie (nicht die Eröffnung dieser Partie, sondern irgendeine). Bei einer Übereinstimmung soll die Partie gelistet werden, dann wird das Array neu gefüllt mit der nächsten Partie und wieder geschaut ob die Eröffnung zur Partie passt. Das Problematische ist, dass die Arrays mit verschiedenen Keys anfangen, das eine Array fängt bei 0 an, das andere bei 1. Also Array1[0] muss mit Array2[1] übereinstimmen und Array1[1] mit Array2[2], und das ganze bis count($Array2).

array_diff scheint mir die Unterschiede anzuzeigen, laut manual.

Ahjo, hab es kurz so gelöst:
PHP:
   $found_match = [];
   
   for ($x=0;$x<=count($opening);$x++) {
        $xi = $x+1;
        if ($movesCleanArray[$x] == $opening[$xi]) {
          $found_match[] = $x;
        }
   }
   
   if (count($found_match) == count($opening)) {
       $matchRows[] = $row;
   }

Das funktioniert richtig, nur habe ich einen neuen Bug in meinem Projekt entdeckt. Aber dazu später... =)
 
jumpin schrieb:
PS: Bei deinem Beispiel fehlt im ersten Array das erste Element (also [0])

Ich denk mal die 0 fehlt, weil ein Schachspiel mit dem 1. Zug beginnt, so muss er bei der Ausgabe nicht immer $i+1 ausgeben sondern kann direkt auf den Schlüssel gehen. In PHP sind Arrays eh immer Hashmaps, wenn er ein array(0=>'X', 5=>'Y', 7 => 'Z') hätte wärs auch egal, wird genauso wie ein assoziatives Array behandelt und mit foreach durchlaufen. For/foreach geben sich auch performancetechnisch rein gar nichts (ich glaub foreach ist sogar etwas schneller).

Ansonsten herzlichen Glückwunsch, in gefühlten 100.000 Zeilen PHP Quelltext habe ich noch nie ein strcmp gesehen, da hast du dir die 'am wenigsten abgegriffene' PHP Funktion rausgesucht ;)

EDIT: ich hab gerade mal geguckt(das hat mich jetzt echt gewundert), ich habe hier ein Projekt mit irgendwas bei 16000 Funktionen, da ist nicht einmal strcmp genutzt worden :D :D :D
Ergänzung ()

smallwall schrieb:
PHP:
   $found_match = [];
   
   for ($x=0;$x<=count($opening);$x++) {
        $xi = $x+1;
        if ($movesCleanArray[$x] == $opening[$xi]) {
          $found_match[] = $x;
        }
   }
   
   if (count($found_match) == count($opening)) {
       $matchRows[] = $row;
   }
Na da mach mal dein Errorreporting an:
du zählst bis zum Ende von $opening und greifst in der Schleife auf das X+1ste Element von $opening zu, das heißt du greifst auf ein Element zu, das es nicht gibt. Da sollte eigentlich eine Notice geworfen werden.

Wenn Array2 immer um einen Schlüssel verschoben ist hier mein angepasster Code:
PHP:
    protected function compareArrays($array1, $array2) {
    foreach($array1 as $key => $value) {
      if(isset($array2[$key+1]) && $array2[$key+1] != $value) {
        return false;
        break;
      }
    }
    return true;
    }
 
Zuletzt bearbeitet von einem Moderator:
Naja, da hast du eigentlich Recht, aber $x läuft ab 0, aber $opening[0] gibt es nicht, das ist das "verschobene" Array. Die Schleife wird count($opening)-mal durchlaufen, $opening fängt bei 1 an, also bei 4 Elementen in $opening muss es 4 mal durchlaufen werden, aber der letzte Key ist 4 bei $opening aber 3 beim anderen Array.
Edit*** aber,aber,aber, omg ist es spät =)
 
Zuletzt bearbeitet:
smallwall schrieb:
Naja, da hast du eigentlich Recht, aber $x läuft ab 0, aber $opening[0] gibt es nicht, das ist das "verschobene" Array. Die Schleife wird count($opening)-mal durchlaufen, $opening fängt bei 1 an, also bei 4 Elementen in $opening muss es 4 mal durchlaufen werden, aber der letzte Key ist 4 bei $opening aber 3 beim anderen Array.
Edit*** aber,aber,aber, omg ist es spät =)

Achso, dann wäre es
PHP:
        protected function compareArrays($array1, $array2) {
        foreach($array1 as $key => $value) {
          if(isset($array2[$key-1]) && $array2[$key-1] != $value) {
            return false;
            break;
          }
        }
        return true;
        }

Bei meiner Funktion. Deine würde laufen, aber der Stil ist eher... naja. Ich bin allgemein kein großer Freund von for-Schleifen in PHP, in 7 von 8 Fällen fährt man mit einer foreach eindeutiger und sicherer. A) hast du dann die Gefahr nicht mehr, dass du auf undefinierte Arrayelemente zugreifst und b) ist es von der performance her eher besser als schlechter. Bei
PHP:
for ($x=0;$x<=count($opening);$x++) {

zählst du zB mit jeder Iteration die Arrayelemente, ich hab jetzt im Kopf dass das Faktor 5-10 langsamer ist als vorher einmal zu zählen und bis zu einer Variablen hochzuzählen bzw einfach ein foreach zu nutzen. Bei dir ist das komplett irrelevant, aber sowas sollte man sich IMHO gar nicht erst angewöhnen.

EDIT: btw, das mag jetzt verwegen klingen, aber warum bringst du die beiden Arrays nicht einfach auf den gleichen Index?
PHP:
$opening[0] = 'bla'; 
array_shift($opening);
//und schon können wir array_diff_assoc benutzen:
if(!count(array_diff_assoc($opening, $movesCleanArray))) {
  echo 'Eröffnung passt';
}

array_diff_assoc gibt alle Werte aus Array1 zurück, die nicht in Array2 vorhanden sind, zusätzlich wird auch der Schlüssel geprüft, macht also genau das, was wir hier zu Fuß in einer Schleife gemacht haben.
 
Zuletzt bearbeitet von einem Moderator:
Ich kenne die Array Funktionen noch nicht so gut. Ich muss nur wissen, ob der Anfang des einen Arrays mit dem anderen Key für Key übereinstimmt.
 
Na array_diff schaut, ob alle Elemente von Array1 in Array2 vorkommen und gibt gegebenfalls Elemente, die in Array2 nicht vorhanden sind zurück. array_diff_assoc macht das selbe, prüft aber zusätzlich die Schlüssel.

-Problem sind die unterschiedlichen Schlüssel, $opening beginnt mit Element 1.
-Also fügt man bei $opening ein Element 0 hinzu $opening[0] = 'bla'; und benutzt dann array_shift.
-array_shift gibt das erste Element eines Arrays zurück und verschiebt die restlichen Elemente nach vorn.
-Dein Element 1 aus $opening wird also zu Element 0, das 'bla' fällt weg.
-Jetzt sind die Arrays schön zu vergleichen. array_diff_assoc gibt, wenn es einen Unterschied gibt, ein Array mit dem Unterschied als Element zurück (bzw den Unterschieden wenns mehrere sind).
-Also prüfen wir mit count(array_diff_assoc()) wie viele Elemente das Array hat, was array_diff_assoc zurückgibt.
-Weils kurz ist hab ich !(count()) benutzt, wenn count == 0 ist ist !0 == true. Also man könnte auch schreiben if(count(...) == 0), sprich es gibt kein Element aus Array1 was mit dem selben Schlüssel und Wert nicht auch in Array2 vorkommt.
Ergänzung ()

Vielleicht nochmal zu den array Funktionen: ich benutze hauptsächlich assoziative Arrays die aus der Datenbank befüllt werden, dazu sind ziemlich wichtig:

Zum Sortieren:
asort, ksort,
Zum Umbauen:
array_flip, array_reverse
Für Stacks:
array_pop/array_push
Allgemein:
in_array, shuffle, count, array_values, foreach

array_shift ist das Gegenteil von array_pop, in der Praxis ist man aber oft besser dran wenn man ein Array mit array_reverse umdreht und dann ein array_pop macht, weil array_shift mit jedem Aufruf das komplette Array umkopieren muss, array_pop nimmt nur das letzte Element weg.

http://php.net/manual/en/ref.array.php

Für den Anfang kommt man mit den Funktionen gut über die Runden.
 
PHP:
$opening = array_values($opening); //Array fängt jetzt bei 0 an.

$opening_count = count($opening);

while ($row = $rows->fetch()) {
    
   $movesCleanArray = unserialize($row["MovesClean"]);
   $found_match = [];
   
   foreach ($opening as $key => $value) {
       if ($value == $movesCleanArray[$key]) {
           $found_match[] = $key;
       }
   }
   if (count($found_match) == $opening_count) {
       $matchRows[] = $row;
   }
}
Habe einen Thread auf Stackoverflow gefunden, dort ist die selbe Frage "Wie lässt man ein Array bei 0 Anfangen". $a = array_values($a); war die Antwort. Diese Lösung scheint mir die optimalste, macht genau das, was ich wollte :) Jetzt gibt es nur noch ein Problem, aber das ist eine komplett andere Baustelle, hängt mit den Problemen der anderen Threads zusammen. Der pgn-parser von AmyBoyd, den r15ch13 mir empfohlen hat, scheint manchmal Probleme zu haben. Also werde ich wohl das Ding selbst nachbauen müssen. Wenn ich preg_match etc. könnte, wäre das bestimmt kein Problem.

Edit: man könnte auch einfach einen Zähler statt des found_match Arrays benutzen, das wäre noch eine Spur effektiver.
PHP:
$opening = array_values($opening);
$opening_count = count($opening);

while ($row = $rows->fetch()) {
    
   $movesCleanArray = unserialize($row["MovesClean"]);
   $found_match = 0;
   
   foreach ($opening as $key => $value) {
       if ($value == $movesCleanArray[$key]) {
           $found_match++;
       }
   }
   if ($found_match == $opening_count) {
       $matchRows[] = $row;
   }
}
 
Zuletzt bearbeitet: (updated)
Zurück
Oben