rekneh: PHP: Multiarray umgekehrt sortieren

Hallo,

ich möchte die gekürzte Version von Usort von (groß nach klein), umgekehrt sortieren zu (klein nach groß)

usort($final_array, function($a, $b) {
    return $b['score'] <=> $a['score'];
});

Meine Idee war ja die Operatoren zu ändern in >=< aber da gab es nur eine Fehlermeldung. Kennt jemand eine Übersicht der Operatoren?

  1. Hilft Dir das Folgende?

    <?php
    $final_array[0]['score']='foo';
    $final_array[1]['score']='Foo';
    $final_array[2]['score']='Bar';
    $final_array[3]['score']='bar';
    
    print_r( $final_array );
    
    usort( $final_array, function( $a, $b ) {
        return $b['score'] <=> $a['score'];
    } );
    
    print_r( $final_array );
    
    function myusort( $arr ) {
    	usort( $arr, function( $a, $b ) {
    		return $b['score'] <=> $a['score'] ;
    	} );
    	return array_reverse( $arr, true );
    }
    print_r( myusort( $final_array) );
    
    [Original]-Array
    (
        [0] => Array
            (
                [score] => foo
            )
    
        [1] => Array
            (
                [score] => Foo
            )
    
        [2] => Array
            (
                [score] => Bar
            )
    
        [3] => Array
            (
                [score] => bar
            )
    
    )
    [Nach Deinem usort] Array
    (
        [0] => Array
            (
                [score] => foo
            )
    
        [1] => Array
            (
                [score] => bar
            )
    
        [2] => Array
            (
                [score] => Foo
            )
    
        [3] => Array
            (
                [score] => Bar
            )
    
    )
    [Nach myusort]Array
    (
        [3] => Array
            (
                [score] => Bar
            )
    
        [2] => Array
            (
                [score] => Foo
            )
    
        [1] => Array
            (
                [score] => bar
            )
    
        [0] => Array
            (
                [score] => foo
            )
    
    )
    
    1. Oder gefällt Dir Rolfs Idee ("Wie kannst Du das Ergebnis des Spaceship-Operatos manipulieren, um die benötigten Werte zu erhalten.") ...

      <?php
      $final_array[0]['score']='foo';
      $final_array[1]['score']='Foo';
      $final_array[2]['score']='Bar';
      $final_array[3]['score']='bar';
      
      usort( $final_array, function( $a, $b ) {
          return 1 == ( $a['score'] <=> $b['score'] );
      } );
      
      print_r( $final_array );
      

      ... besser?

      Array
      (
          [0] => Array
              (
                  [score] => Bar
              )
      
          [1] => Array
              (
                  [score] => Foo
              )
      
          [2] => Array
              (
                  [score] => bar
              )
      
          [3] => Array
              (
                  [score] => foo
              )
      
      )
      
      1. Hallo ursus,

        dir fällt aber schon auf, dass dein Sortierergebnis aufsteigend ist statt absteigend? Das war nicht das Ziel. Abgesehen davon ist 1 == ... definitiv der falsche Ansatz, um mit einer dreiwertigen Situation umzugehen.

        Rolf

        --
        sumpsi - posui - clusi
        1. Ich dachte, er will zuerst Großbuchstaben. Du meinst, er will:

          <?php
          $final_array[0]['score']='bar';
          $final_array[1]['score']='Foo';
          $final_array[2]['score']='Bar';
          $final_array[3]['score']='foo';
          
          usort( $final_array, function( $a, $b ) {
          	echo 
          	    '"' . $a['score'] . '" <=> "' . $b['score'] . '" : '
          	    . ( $a['score'] <=> $b['score'] )  
          		  . ' => ' 
          		  . ( -1 * ( $a['score'] <=> $b['score'] ) ) 
          		  . PHP_EOL;
          
              return -1 * ( $a['score'] <=> $b['score'] );
          } );
          
          print_r( $final_array );
          

          mit diesem Ergebnis?

          "bar" <=> "Foo" : 1 => -1
          "Foo" <=> "Bar" : 1 => -1
          "Bar" <=> "foo" : -1 => 1
          "Foo" <=> "foo" : -1 => 1
          "bar" <=> "foo" : -1 => 1
          Array
          (
              [0] => Array
                  (
                      [score] => foo
                  )
          
              [1] => Array
                  (
                      [score] => bar
                  )
          
              [2] => Array
                  (
                      [score] => Foo
                  )
          
              [3] => Array
                  (
                      [score] => Bar
                  )
          
          )
          
          1. Hallo ursus,

            nein, sorry, du hattest recht. Gesucht ist Bar, Foo, bar, foo.

            Trotzdem verstehe ich noch nicht, warum 1==($a<=>$b) die Aufgabenstellung löst.

            Rolf

            --
            sumpsi - posui - clusi
            1. nein, sorry, du hattest recht. Gesucht ist Bar, Foo, bar, foo.

              Sicher bin ich mir da gar nicht. Die Aufgabestellung ist durchaus mehrdeutig und Rekneh hat sich im Forum nicht gemeldet um das klarzustellen.

              Trotzdem verstehe ich noch nicht, warum 1==($a<=>$b) die Aufgabenstellung löst.

              Da bin ich bei Dir. Auch meine Tests lassen mich mit dem Rätsel zurück, WARUM das geht. (Insofern kann ich Vorgehen nicht ernsthaft empfehlen!) Auch die Ausgaben geben mir keine wirkliche Hilfe.

              <?php
              
              $final_array[]['score']='foo';
              $final_array[]['score']='quirx';
              $final_array[]['score']='Quirx';
              $final_array[]['score']='Bar';
              $final_array[]['score']='bar';
              $final_array[]['score']='Foo';
              
              usort( $final_array, function( $a, $b ) {
              	echo 
              	    '"' . $a['score'] . '" <=> "' . $b['score'] . '" : '
              	    . ( $a['score'] <=> $b['score'] )  
              		  . ' => ' 
              		  . (int)( 1 == ( $a['score'] <=> $b['score'] ) ) 
              		  . PHP_EOL;
                  return (int)1 == ( $a['score'] <=> $b['score'] );
              } );
              
              print_r( $final_array );
              
              "foo" <=> "quirx" : -1 => 0
              "quirx" <=> "Quirx" : 1 => 1
              "foo" <=> "Quirx" : 1 => 1
              "quirx" <=> "Bar" : 1 => 1
              "foo" <=> "Bar" : 1 => 1
              "Quirx" <=> "Bar" : 1 => 1
              "quirx" <=> "bar" : 1 => 1
              "foo" <=> "bar" : 1 => 1
              "Quirx" <=> "bar" : -1 => 0
              "quirx" <=> "Foo" : 1 => 1
              "foo" <=> "Foo" : 1 => 1
              "bar" <=> "Foo" : 1 => 1
              "Quirx" <=> "Foo" : 1 => 1
              "Bar" <=> "Foo" : -1 => 0
              Array
              (
                  [0] => Array
                      (
                          [score] => Bar
                      )
              
                  [1] => Array
                      (
                          [score] => Foo
                      )
              
                  [2] => Array
                      (
                          [score] => Quirx
                      )
              
                  [3] => Array
                      (
                          [score] => bar
                      )
              
                  [4] => Array
                      (
                          [score] => foo
                      )
              
                  [5] => Array
                      (
                          [score] => quirx
                      )
              
              )
              
              

              Ich habe keinen Fehler provozieren können. Auch mit dem ganz großen Test nicht:

              <?php
              
              $ar     = [ 'foo', 'qix', 'Bar', 'Qix', 'bar', 'Foo' ];
              $sorted = [ 'Bar', 'Foo', 'Qix', 'bar', 'foo', 'qix' ];
              
              $z=0;
              while ( $z < 1000 ) {
              	shuffle( $ar ); # Array oft genug mixen statt nachdenken...
              	if ( myUsort( $ar ) != $sorted ) {
              	   print_r( $ar );
              	   break;
              	}
              	echo $z++ . ' ';
              }
              
              function myUsort( $ar ) {
              	usort( $ar, function( $a, $b ) {
              		/*
              		echo 
              			'"' . $a . '" <=> "' . $b . '" : '
              			. ( $a <=> $b )  
              			  . ' => ' 
              			  . (int)( 1 == ( $a <=> $b ) ) 
              			  . PHP_EOL;
              	    */
              		return (int)1 == ( $a <=> $b );
              	} );
              	return $ar;
              }
              

              Theoretisch sorgt usort nur dann für eine Umsortierung, wenn das Return der aufgerufenen Funktion 1 ist. Bei 0 und -1 macht usort gar nichts. Ich hab das mit allem möglichen Zeug probiert:

              <?php
              $or = [ "Bar", "foo" ];
              
              $ar = $or;
              usort( $ar, function( $a, $b ) {
                 echo "$a, $b" . PHP_EOL;
                 return -1;
              } );
              echo '-1: ' . implode (', ', $ar) . PHP_EOL . PHP_EOL;
              
              # Bar, foo
              # -1: Bar, foo
              
              $ar = $or;
              usort( $ar, function( $a, $b ) {
                 echo "$a, $b" . PHP_EOL;
                 return 0;
              } );
              echo ' 0: ' . implode (', ', $ar) . PHP_EOL . PHP_EOL;
              
              # Bar, foo
              # 0: Bar, foo
              
              $ar = $or;
              usort( $ar, function( $a, $b ) {
                 echo "$a, $b" . PHP_EOL;
                 return 1;
              } );
              echo ' 1: ' . implode (', ', $ar) . PHP_EOL . PHP_EOL;
              
              # Bar, foo
              # 1: foo, Bar
              

              So lange ich von einem klügeren nichts besseres erfahre (die Dokumentation sagt gar nichts!) Denke ich: "Hm. Entweder ein Bug oder ein Easteregg.". Ich habe auch schon erfolgsfrei nach "usort" in C und CPP gesucht, weil PHP manche Standard-Funktionen von C/CPP nur zu "wrappen" scheint.

              1. Hallo ursus,

                patsch - meine Stirn ist jetzt etwas flacher.

                Fazit: sehe ich auch so, usort behandelt -1 und 0 gleich. Das passt auch zu den üblichen Sortieralgorithmen, die entscheiden binär auf "tauschen" oder "nicht tauschen". Weil $a<=>$b == 1 bedeutet, dass $a > $b ist, ist nur dann eine Fehlstellung im Array vorhanden, die einen Tausch erfordert. Typischer Fall von DUH!

                Der Witz ist nur: Es ändert gar nichts. Ob man $a <=> $b oder 1 == ($a <=> $b) verwendet - die Sortierreihenfolge ist gleich. Erst die Großbuchstaben, dann die Kleinbuchstaben. Aufsteigende Sortierung gemäß ASCII-Code. Mit $b <=> $a oder -($a <=> $b) erhält man absteigende Sortierung nach Ascii-Code.

                Aber was will unser Rückwärtshenker nun eigentlich? Der array_reverse hat geholfen? D.h. die von einem usort gelieferte Sortierung wird gespiegelt? Das ist nun eigentlich zu aufwändig, es sei denn die usort-Sortierung liegt aus anderen Gründen eh schon vor. Wenn man mit usort aufsteigend sortiert und dann reversiert, ist es wohl besser, gleich absteigend zu sortieren. Wie das geht - siehe oben.

                Mein Gedanke war ja die ganze Zeit, dass alphabetisch sortiert werden sollte, aber mit den kleinbuchstaben vor den GROSSbuchstaben (sozusagen native EBCDIC Reihenfolge). Und das sähe ganz anders aus.

                Rolf

                --
                sumpsi - posui - clusi
                1. Hallo Rolf B,

                  Typischer Fall von DUH!

                  Was bedeutet „DUH!“? D'oh!?

                  Bis demnächst
                  Matthias

                  --
                  Pantoffeltierchen haben keine Hobbys.
                  1. Hallo Matthias,

                    ich hatte das in diversen Geschichten gelesen ohne eine Übersetzung zu haben. D'oh musste ich jetzt erstmal suchen. Es gibt bei dict.Leo.org Eine Diskussion, nach der das das gleiche ist. D'oh wäre dann die formalisierte Variante.

                    Mit der Bedeutung lag ich aber richtig: „Was für eine blöde Frage“...

                    Rolf

                    --
                    sumpsi - posui - clusi
                2. patsch - meine Stirn ist jetzt etwas flacher.

                  Meine auch. Das 1 == ( $a <=> $b ) bringt bei usort natürlich nichts. Aber statt array_reverse() nachträglich anzuwenden kann man die Paare tauschen - und vor allem auf einen billigeren Vergleich zurückgreifen. Auch wenn das erst bei ein paar tausend Items fühlbar wird: Wir wollen ja nicht das Rechenzentrum heizen um es dann wieder aufwendig zu kühlen. (Ich weiß, warum der letzte Sommer so heiß war...)

                  <?php
                  $arO    = [ 'foo', 'qix', 'Bar', 'Qix', 'bar', 'Foo' ];
                  
                  $arC    = $arO;
                  usort( $arC, function( $a, $b ) {
                  		return $a <=> $b;
                  	} );
                  
                  echo '-------------------------------' . PHP_EOL;	
                  echo 'Normaler Vergleich:'             . PHP_EOL;
                  echo ' $a <=> $b'                      . PHP_EOL;
                  echo '-------------------------------' . PHP_EOL;	
                  echo implode( ', ', $arC ) . PHP_EOL . PHP_EOL;
                  
                  
                  $arC    = $arO;
                  usort( $arC, function( $a, $b ) {
                  		return 1 == ( $a <=> $b );
                  	} );
                  
                  echo '-------------------------------' . PHP_EOL;	
                  echo 'Ausschluss von -1 als Return:'  . PHP_EOL;	
                  echo '1 == ( $a <=> $b )'              . PHP_EOL;
                  echo '-------------------------------' . PHP_EOL;	
                  echo implode( ', ', $arC ) . PHP_EOL . PHP_EOL;
                  
                  
                  $arC    = $arO;
                  usort( $arC, function( $a, $b ) {
                  		return $b <=> $a;
                  	} );
                  echo '-------------------------------' . PHP_EOL;
                  echo 'Spaceship mit Reverse-Params:'   . PHP_EOL;
                  echo ' $b <=> $a'                      . PHP_EOL;
                  echo '-------------------------------' . PHP_EOL;
                  echo implode( ', ', $arC ) . PHP_EOL . PHP_EOL;
                  
                  
                  $arC    = $arO;
                  usort( $arC, function( $a, $b ) {
                  		return -1 * ( $a <=> $b );
                  	} );
                  echo '-------------------------------' . PHP_EOL;
                  echo 'Multiplikation mit -1:'          . PHP_EOL;
                  echo ' -1 * ( $a <=> $b )'             . PHP_EOL;
                  echo '-------------------------------' . PHP_EOL;
                  echo implode( ', ', $arC ) . PHP_EOL . PHP_EOL;
                  
                  $arC    = $arO;
                  usort( $arC, function( $a, $b ) {
                  		return (int)( $a > $b );
                  	} );
                  echo '-------------------------------' . PHP_EOL;
                  echo 'Einfacher Vergleich'             . PHP_EOL;
                  echo ' (int)$a > $b'                   . PHP_EOL;
                  echo '-------------------------------' . PHP_EOL;
                  echo implode( ', ', $arC ) . PHP_EOL . PHP_EOL;
                  
                  $arC    = $arO;
                  usort( $arC, function( $a, $b ) {
                  		return (int)( $a < $b );
                  	} );
                  echo '-------------------------------' . PHP_EOL;
                  echo 'Einfacher Vergleich, reverse'    . PHP_EOL;
                  echo ' (int)$a < $b'                   . PHP_EOL;
                  echo '-------------------------------' . PHP_EOL;
                  echo implode( ', ', $arC ) . PHP_EOL . PHP_EOL;
                  

                  Ausgaben:

                  -------------------------------
                  Normaler Vergleich:
                   $a <=> $b
                  -------------------------------
                  Bar, Foo, Qix, bar, foo, qix
                  
                  -------------------------------
                  Ausschluss von -1 als Return:
                  1 == ( $a <=> $b )
                  -------------------------------
                  Bar, Foo, Qix, bar, foo, qix
                  
                  -------------------------------
                  Spaceship mit Reverse-Params:
                   $b <=> $a
                  -------------------------------
                  qix, foo, bar, Qix, Foo, Bar
                  
                  -------------------------------
                  Multiplikation mit -1:
                   -1 * ( $a <=> $b )
                  -------------------------------
                  qix, foo, bar, Qix, Foo, Bar
                  
                  -------------------------------
                  Einfacher Vergleich
                   (int)$a > $b
                  -------------------------------
                  Bar, Foo, Qix, bar, foo, qix
                  
                  -------------------------------
                  Einfacher Vergleich, reverse
                   (int)$a < $b
                  -------------------------------
                  qix, foo, bar, Qix, Foo, Bar
                  

                  Am billigsten ist also die letzte Methode.

        2. Hi,

          dir fällt aber schon auf, dass dein Sortierergebnis aufsteigend ist statt absteigend? Das war nicht das Ziel. Abgesehen davon ist 1 == ... definitiv der falsche Ansatz, um mit einer dreiwertigen Situation umzugehen.

          der dritte Wert kommt aber nur zum Tragen, wenn im Array identische Werte vorhanden wären.

          Und selbst dann wäre es bei einfachen Werten (die praktisch nur aus dem Key bestehen, nachdem sortiert wird) egal. Denn ob Du 1, 1, 2, 3 hast oder 1, 1, 2, 3 ist relativ egal.

          Relevant wird das erst, wenn an dem Sortier-Key noch andere Daten dranhängen.

          cu,
          Andreas a/k/a MudGuard

          1. Hallo MudGuard,

            ja, hab ich mit einem zweiten Key im Array ausprobiert. usort zeigt bei kleinen Arrays ein stabiles Verhalten, d.h. die Elemente blieben bei einer Rückgabe von -1 oder 0 in ihrer Ursprungsreihenfolge. Bei einem etwas größeren Array wurde dann durcheinandergewürfelt, offenbar wechselt usort dann den Algorithmus.

            Und war jedesmal egal, ob ich $a <=> $b oder ($a<=>$b)==1 verwendete.

            Rolf

            --
            sumpsi - posui - clusi
    2. Sorry für die späte Rückmeldung. Der Trick über "array_reverse" ist super, da wäre ich nie drauf gekommen. Und ja es funktioniert.

      1. Sorry für die späte Rückmeldung. Der Trick über "array_reverse" ist super, da wäre ich nie drauf gekommen. Und ja es funktioniert.

        Leider heizt der auch die Gegend unnötig auf. Das geht klimafreundlicher!.

  2. Hallo rekneh,

    es gibt keinen X-Wing Operator >=<. Nur den Spaceship-Operator <=>.

    Aber da kommst Du selbst drauf. Überlege mal: Was liefert Dir der Spaceship-Operator, und was muss usort für eine absteigende Sortierung bekommen? Wie kannst Du das Ergebnis des Spaceship-Operatos manipulieren, um die benötigten Werte zu erhalten.

    Tipp: betrachte die Fälle a < b, a == b und a > b einzeln. Was liefert <=>, was muss usort bekommen?

    Rolf

    --
    sumpsi - posui - clusi
  3. Tach!

    ich möchte die gekürzte Version von Usort von (groß nach klein), umgekehrt sortieren zu (klein nach groß)

    Meine Idee war ja die Operatoren zu ändern in >=< aber da gab es nur eine Fehlermeldung.

    Wie wäre es mit Ändern der Operanden?

    Kennt jemand eine Übersicht der Operatoren?

    Ja klar, gibts in vermutlich jeder Programmiersprachendokumentation, auf alle Fälle in der von PHP.

    dedlfix.

    1. Hallo dedlfix,

      ich bin mal nicht so: http://php.net/manual/de/language.operators.php

      Rolf

      --
      sumpsi - posui - clusi