Array Key Sortierung (uksort) mit cm

sakrileg1980

Gesperrt
Hallo zusammen,

ich habe ein Problem mit einer benutzedefinierten Sortierung die ich versucht habe zu bauen. Ich habe ein Array mit Größen (allerdings sind diese in cm angegeben) in der Form "a x b cm". Diese sollen jetzt von klein nach groß sortiert werden ABER beide Teile (a und b) - also so
10 x 10
20 x 10
20 x 20
20 x 30
30 x 10
usw.

Hier der Code:
PHP:
$data['test1'] = 1;
$data['test2'] = 2;
$data['test3'] = 3;
$data['test4'] = 4;
$data['test5'] = 5;

$testarray['10 x 10]'] = $data;
$testarray['50 x 30'] = $data;
$testarray['50 x 20'] = $data;
$testarray['50 x 10'] = $data;
$testarray['150 x 80'] = $data;
$testarray['80 x 50'] = $data;
$testarray['90 x 60'] = $data;
$testarray['120 x 90'] = $data;
$testarray['120 x 60'] = $data;
$testarray['100 x 100'] = $data;
$testarray['100 x 60'] = $data;
$testarray['190 x 50'] = $data;
$testarray['150 x 50'] = $data;

function array_sort_function($a, $b) {
    $str_a = explode(' ', $a);
    $str_full_a = $str_a[0].$str_a[2];
    $str_b = explode(' ', $b);
    $str_full_b = $str_b[0].$str_b[2];
  
    if ($str_full_a > $str_full_b) {
        return 1;
    }
}
uksort($testarray, 'array_sort_function');

Das funzt auf dem 1. Blick auch ganz gut aber eben nicht in allen Fällen. Mit dieser Funktion landet 100 x 100 nach 120 x 90, weil die Zahl an sich größer ist. Ich habe es auch schon versucht zu unterteilen, also zuerst nach a und dann nach b zu fragen aber dann haut das alles überhaupt nicht mehr hin.
Hat jemand 'ne Idee?
 
Hallo sakrileg1980,

das Problem ist, dass $str_full_a und $str_full_b einfach nur Zeichenketten sind, die das Ergebnis nur noch mehr "verhauen".

Folgendes sollte zur Lösung führen (ungetestet).
PHP:
$data['test1'] = 1;
$data['test2'] = 2;
$data['test3'] = 3;
$data['test4'] = 4;
$data['test5'] = 5;
$testarray['10 x 10'] = $data;
$testarray['50 x 30'] = $data;
$testarray['50 x 20'] = $data;
$testarray['50 x 10'] = $data;
$testarray['150 x 80'] = $data;
$testarray['80 x 50'] = $data;
$testarray['90 x 60'] = $data;
$testarray['120 x 90'] = $data;
$testarray['120 x 60'] = $data;
$testarray['100 x 100'] = $data;
$testarray['100 x 60'] = $data;
$testarray['190 x 50'] = $data;
$testarray['150 x 50'] = $data;
function array_sort_function($a, $b) {
  $str_a = explode(' x ', $a);
  $str_full_a = $str_a[0];
  $str_b = explode(' x ', $b);
  $str_full_b = $str_b[0];

  if (intval($str_full_a) > intval($str_full_b)) {
  return 1;
  }
}
uksort($testarray, 'array_sort_function');
 
Danke für die Antwort aber nein, das hilft nicht. Ich habe das auch zuerst so gehabt aber bemerkt, dass php es trotzdem als Zahl erkennt (scheint wohl nur bei älteren Versionen zu Problemen zu führen). Er sortiert es ja, nur ist die Sortierfunktion unvollständig, da ich nicht das gewünschte Ergebnis kriege.
 
PHP:
<?php

$data['test1'] = 1;
$data['test2'] = 2;
$data['test3'] = 3;
$data['test4'] = 4;
$data['test5'] = 5;

$testarray['10 x 10']   = $data;
$testarray['50 x 30']   = $data;
$testarray['50 x 20']   = $data;
$testarray['50 x 10']   = $data;
$testarray['150 x 80']  = $data;
$testarray['80 x 50']   = $data;
$testarray['90 x 60']   = $data;
$testarray['120 x 90']  = $data;
$testarray['120 x 60']  = $data;
$testarray['100 x 100'] = $data;
$testarray['100 x 60']  = $data;
$testarray['190 x 50']  = $data;
$testarray['150 x 50']  = $data;

function array_sort_function($a, $b) {
  $tmp   = explode(' x ', $a);
  $aDim1 = (int) $tmp[0];
  $aDim2 = (int) $tmp[1];

  $tmp   = explode(' x ', $b);
  $bDim1 = (int) $tmp[0];
  $bDim2 = (int) $tmp[1];

  if ($aDim1 !== $bDim1) {
    return ($aDim1 < $bDim1) ? -1 : 1;
  }

  if ($aDim2 !== $bDim2) {
    return ($aDim2 < $bDim2) ? -1 : 1;
  }

  return 0;
}

uksort($testarray, 'array_sort_function');

var_dump(array_keys($testarray));

So entspricht es deinen Vorgaben. (Wobei 100x100 im Grunde größer ist als 120x60.)

Ausgabe:

Code:
array(13) {
  [0]=>
  string(7) "10 x 10"
  [1]=>
  string(7) "50 x 10"
  [2]=>
  string(7) "50 x 20"
  [3]=>
  string(7) "50 x 30"
  [4]=>
  string(7) "80 x 50"
  [5]=>
  string(7) "90 x 60"
  [6]=>
  string(8) "100 x 60"
  [7]=>
  string(9) "100 x 100"
  [8]=>
  string(8) "120 x 60"
  [9]=>
  string(8) "120 x 90"
  [10]=>
  string(8) "150 x 50"
  [11]=>
  string(8) "150 x 80"
  [12]=>
  string(8) "190 x 50"
}
 
@mermshaus Vielen Dank, das funzt genau wie gewünscht.

Ja, Flächenmäßig bzw. von der Größe der Zahl liegt es weiter vorne, weshalb ich ja keine herkömmlichen Sortierungen nutzen konnte.

Könntest du mir nochmal kurz erklären, warum du die -1, 1 und 0 Werte returnst und was sie genau bewirken? Gerade da hatte ich halt Verständnisprobleme, weil php.net da nicht sehr ausschweifend wird.
Also worin liegt der Unterschied zwischen 0 und -1? Was wird in welchem Fall zurück gegeben, a oder b?
 
Hi

bei manchen Sortierfunktionen (wie uksort) kann man ja eine Vergleichsfunktion für zwei Elemente übergeben (wie im Beispiel oben). Die Funktion muss im Wesentlichen untersuchen, welches der zwei übergebenen Elemente in der gewünschten Sortierreihenfolge das Vordere ist, und das mit dem Returnwert bekanntgeben. Die Funktion wird dann von uksort während der Arraysortierung mehrmals aufgerufen; anhand der Werte wird entschieden, welches Arrayelement wohin geschoben wird.

Für derartige Funktionen ist es in mehreren Programmiersprachen (nicht nur PHP) üblich, dass sie
* eine Zahl kleiner 0 zurückgeben, wenn der erste Parameter vor den zweiten gehört,
* eine Zahl größer 0 wenn der zweite vor den ersten muss,
* und 0 selber wenn die zwei Parameter "gleich" sind bzw. die Reihenfolge egal ist.
uksort wurde auch passend dazu programmiert. Die Wahl der Werte(bereiche) hat keine tiefere Bedeutung, wurde einfach so "erfunden". Hätte auch 1/2/3 sei können.

(Genaugenommen hat es schon einen Sinn: Wenn man einfach nur zwei (ganze) Zahlen vergleichen will, so dass die kleinere Zahl eben vor die größere kommt, kann man einfach die eine Zahl minus die andere nehmen, statt drei "if" zu schreiben. Ist weniger Code, und der Prozessor kann das einzelne Minus auch schneller abarbeiten)
 
Ja, man könnte den Sortierteil in diesem Fall auch so schreiben:

PHP:
if ($aDim1 !== $bDim1) {
  return $aDim1 - $bDim1;
}

return $aDim2 - $bDim2;

Das funktioniert aber bei Float-Werten nicht zuverlässig, weshalb ich mir auch bei Integer-Vergleichen angewöhnt habe, explizit -1, 0 und 1 zurückzugeben.

Returning non-integer values from the comparison function, such as float, will result in an internal cast to integer of the callback's return value. So values such as 0.99 and 0.1 will both be cast to an integer value of 0, which will compare such values as equal.

- http://php.net/manual/en/function.usort.php

Ich finde ein return ($a < $b) ? -1 : 1; zudem besser verständlich. Das kann man 1:1 als „wenn erstes Element kleiner als zweites Element, dann sortiere es nach oben, sonst nach unten“ lesen. Bei return $a - $b; muss zumindest ich arg überlegen, was das tut. Vor allem auch dann, wenn man die Sortierung umkehren will (absteigend statt aufsteigend) und dann return $b - $a; schreibt. return ($a > $b) ? -1 : 1; finde ich expressiver.

PS: In meinem Code in #4 könnte man in Zeile 40 auch eine LogicException werfen (statt return 0;), weil es keine zwei identischen Keys geben kann. Sobald die Zeile erreicht wird, hat sich irgendwo in einem Key überzähliger Whitespace eingeschlichen oder so.
 

Neue Beiträge

Zurück