wake-up-neo.com

Sortieren eines PHP-Arrays von Arrays nach benutzerdefinierter Reihenfolge

Ich habe ein Array von Arrays:

Array ( 
    [0] => Array (
        [id] = 7867867,
        [title] = 'Some Title'),
    [1] => Array (
        [id] = 3452342,
        [title] = 'Some Title'),
    [2] => Array (
        [id] = 1231233,
        [title] = 'Some Title'),
    [3] => Array (
        [id] = 5867867,
        [title] = 'Some Title')
)

Die Notwendigkeit, in einer bestimmten Reihenfolge zu gehen:

  1. 3452342
  2. 5867867
  3. 7867867
  4. 1231233

Wie würde ich das machen? Ich habe bereits Arrays sortiert und habe viele andere Beiträge darüber gelesen, aber sie sind immer vergleichsbasiert (d. H. ValueA <valueB).

Hilfe wird geschätzt.

44
Honus Wagner

Mit usort() können Sie genau festlegen, wie das Array sortiert werden soll. In diesem Fall kann das $order-Array innerhalb der Vergleichsfunktion verwendet werden.

Das folgende Beispiel verwendet eine closure , um das Leben zu erleichtern.

$order = array(3452342, 5867867, 7867867, 1231233);
$array = array(
    array('id' => 7867867, 'title' => 'Some Title'),
    array('id' => 3452342, 'title' => 'Some Title'),
    array('id' => 1231233, 'title' => 'Some Title'),
    array('id' => 5867867, 'title' => 'Some Title'),
);

usort($array, function ($a, $b) use ($order) {
    $pos_a = array_search($a['id'], $order);
    $pos_b = array_search($b['id'], $order);
    return $pos_a - $pos_b;
});

var_dump($array);

Der Schlüssel zu dieser Funktion besteht darin, die Werte, die verglichen werden, zu haben, die Positionen der Variablen ids innerhalb des Arrays $order.

Die Vergleichsfunktion ermittelt die Positionen der IDs von zwei Elementen, die im $order-Array verglichen werden sollen. Wenn $a['id'] vor $b['id'] im $order-Array steht, ist der Rückgabewert der Funktion negativ ($a ist weniger, also "floats" nach oben). Wenn $a['id'] nach $b['id'] kommt, gibt die Funktion eine positive Zahl zurück ($a ist größer, also "sinkt"). 

Schließlich gibt es keinen besonderen Grund für die Verwendung eines Verschlusses. Es ist einfach mein Weg, diese Art von Wegwerffunktionen schnell zu schreiben. Es könnte auch eine normale benannte Funktion verwendet werden.

93
salathe

Antwort von salathe für diese zusätzliche Anforderung erweitern:

Was passiert nun, wenn ich Elemente zum Array hinzufüge und nicht zur Sortierung? ICH Es ist mir egal, in welcher Reihenfolge sie erscheinen, solange es nach den Einsen geht dass ich angegeben habe.

Sie müssen zwei zusätzliche Bedingungen in der Sortierfunktion hinzufügen:

  1. Ein "Nicht-Pflege" -Element muss als größer angesehen werden als "Fürsorge" -Elemente
  2. Zwei Gegenstände, die sich nicht interessieren, müssen als gleichwertig betrachtet werden

Der überarbeitete Code wäre also:

$order = array(
    3452342,
    5867867,
    7867867,
    1231233
);
$array = array(
    array("id" => 7867867, "title" => "Must Be #3"),
    array("id" => 3452342, "title" => "Must Be #1"),
    array("id" => 1231233, "title" => "Must Be #4"),
    array("id" => 5867867, "title" => "Must Be #2"),
    array("id" => 1111111, "title" => "Dont Care #1"),
    array("id" => 2222222, "title" => "Dont Care #2"),
    array("id" => 3333333, "title" => "Dont Care #3"),
    array("id" => 4444444, "title" => "Dont Care #4")
);
function custom_compare($a, $b){
    global $order;
    $a = array_search($a["id"], $order);
    $b = array_search($b["id"], $order);
    if($a === false && $b === false) { // both items are dont cares
        return 0;                      // a == b
    }
    else if ($a === false) {           // $a is a dont care item
        return 1;                      // $a > $b
    }
    else if ($b === false) {           // $b is a dont care item
        return -1;                     // $a < $b
    }
    else {
        return $a - $b;
    }
}
shuffle($array);  // for testing
var_dump($array); // before
usort($array, "custom_compare");
var_dump($array); // after

Ausgabe:

Before                         |  After
-------------------------------+-------------------------------
array(8) {                     |  array(8) {
  [0]=>                        |    [0]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(4444444)               |      int(3452342)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #4"  |      string(10) "Must Be #1"
  }                            |    }
  [1]=>                        |    [1]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(3333333)               |      int(5867867)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #3"  |      string(10) "Must Be #2"
  }                            |    }
  [2]=>                        |    [2]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(1231233)               |      int(7867867)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #4"    |      string(10) "Must Be #3"
  }                            |    }
  [3]=>                        |    [3]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(1111111)               |      int(1231233)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #1"  |      string(10) "Must Be #4"
  }                            |    }
  [4]=>                        |    [4]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(5867867)               |      int(2222222)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #2"    |      string(12) "Dont Care #2"
  }                            |    }
  [5]=>                        |    [5]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(2222222)               |      int(1111111)
    ["title"]=>                |      ["title"]=>
    string(12) "Dont Care #2"  |      string(12) "Dont Care #1"
  }                            |    }
  [6]=>                        |    [6]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(3452342)               |      int(3333333)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #1"    |      string(12) "Dont Care #3"
  }                            |    }
  [7]=>                        |    [7]=>
  array(2) {                   |    array(2) {
    ["id"]=>                   |      ["id"]=>
    int(7867867)               |      int(4444444)
    ["title"]=>                |      ["title"]=>
    string(10) "Must Be #3"    |      string(12) "Dont Care #4"
  }                            |    }
}                              |  }
14
Salman A

Sie müssen Ihre eigene Vergleichsfunktion definieren und usort oder uasort verwenden, wenn Sie die Indexzuordnung beibehalten möchten.

4
Nev Stokes

Die anderen Antworten, die Methoden mit iterierten Aufrufen von array_search() verwenden, sind nicht so effizient wie sie sein können. Durch die Umstrukturierung/Umkehrung des Lookup-Arrays "order" können Sie alle array_search()-Aufrufe vollständig ausschließen. Dadurch wird Ihre Aufgabe wesentlich effizienter und kürzer. Ich verwende den modernsten "Raumschiffoperator" (<=>), aber frühere Techniken funktionieren für die Vergleichslinie gleich.

Methode # 1 - usort wenn alle id-Werte in $order vorhanden sind ( Demo )

$order=array_flip([3452342,5867867,7867867,1231233]);  // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$array=[
    ['id'=>7867867,'title'=>'Some Title'],
    ['id'=>3452342,'title'=>'Some Title'],
    ['id'=>1231233,'title'=>'Some Title'],
    ['id'=>5867867,'title'=>'Some Title']
];

usort($array,function($a,$b)use($order){
    return $order[$a['id']]<=>$order[$b['id']];
    // when comparing ids 3452342 & 1231233, the actual comparison is 0 vs 3
});
// uasort() if you want to preserve keys

var_export($array);

Methode # 2 - usort wenn einige id-Werte nicht in $order vorhanden sind ( Demo )
* Beachten Sie, isset() ist weniger teuer als array_search().

$order=array_flip([3452342,5867867,7867867,1231233]);  // restructure with values as keys, and keys as order (ASC)
// generating $order=[3452342=>0,5867867=>1,7867867=>2,1231233=>3];
$outlier=1+max($order);
// generating $outlier=4
$array=[
    ['id'=>7867867,'title'=>'Some Title'],
    ['id'=>3452342,'title'=>'Some Title'],
    ['id'=>'foo','title'=>'Some Title'],
    ['id'=>1231233,'title'=>'Some Title'],
    ['id'=>'bar','title'=>'Some Title'],
    ['id'=>5867867,'title'=>'Some Title']
];

usort($array,function($a,$b)use(&$order,$outlier){  // make $order modifiable with &
    if(!isset($order[$a['id']])){$order[$a['id']]=$outlier;}  // update lookup array with [id]=>[outlier number]
    if(!isset($order[$b['id']])){$order[$b['id']]=$outlier;}  // and again
    return $order[$a['id']]<=>$order[$b['id']];
});

var_export($array);

Alternative Methode # 2 - usort wenn einige id-Werte nicht in $order vorhanden sind

... Ich möchte auch erwähnen, dass in manchen Fällen das Vermeiden des wiederholten Doppelaufrufs von isset() weniger attraktiv sein kann, als das $order-Array vor dem Aufruf von usort() vollständig vorzubereiten.

Dieser Einzeiler stellt sicher, dass keine id-Werte fehlen, sodass keine andere als die Vergleichslinie innerhalb der Sortierfunktion erforderlich ist. ( Full Snippet Demo )

$order=array_replace(array_fill_keys(array_column($array,'id'),$outlier),$order);
3
mickmackusa

@salathe Für diejenigen von euch, denen es schwer fällt zu verstehen, was salathe's usort macht: 

Jeder Gegenstand in $ -Array ist ein 'Champion' in einem Turnier, der am Anfang eines neuen Arrays steht (außer, dass er die Nummer 1 ist, außer, dass er die Nummer 0 ist).

$ a ist der Heimsieger und $ b der gegnerische Meister in einem Match. 

$ pos_a und $ pos_b aus dem Callback sind die Attribute, die im Kampf um die Champions a und b verwendet werden. In diesem Fall ist dieses Attribut der Index der Champions-ID in $ order. 

Dann gibt es den Kampf bei der Rückkehr. Jetzt schauen wir, ob es besser ist, mehr oder weniger des Attributs zu haben. In einer Usort-Schlacht will der Heimchampion eine negative Zahl, damit er früher im Feld sein kann. Der Auswärtssieger will eine positive Zahl. Und wenn es eine 0 gibt, ist es ein Unentschieden. 

Nach dieser Analogie, wenn das Auswärts-Champions-Attribut (Index in $ -Reihe) vom Attribut der Heimmannschaften abgezogen wird, ist die Wahrscheinlichkeit, dass das Auswärts-Champions-Attribut zunimmt, umso geringer, je positiver es ist. Wenn Sie jedoch die Verwendung der Attribute umkehren, wird jetzt das Attribut des Heimmeisters von dem des Auswärtsmeisters abgezogen. In diesem Fall führt eine größere Anzahl für den Auswärtssieger dazu, dass das Spiel mit einer positiven Zahl endet.

Code würde folgendermaßen aussehen:

anmerkung: Der Code wird viele Male ausgeführt, ähnlich wie bei einem echten Turnier viele Schlachten, um zu entscheiden, wer zuerst kommt (dh 0/Arrayanfang).

//tournament with goal to be first in array
    usort($champions, function ($home, $away) use ($order) {
        $home_attribute = array_search($a['id'], $order);
        $away_attribute = array_search($b['id'], $order);
        //fight with desired outcome for home being negative and away desiring positive
        return $home_attribute - $away_attribute;
    });
1
Jason Basanese

Ohne Sorte kannst du es auch bekommen.

Es gibt keine doppelte ID;

<?php

    $order = array(3452342, 5867867, 7867867, 1231233);
    $array = array(
        array('id' => 7867867, 'title' => 'Some Title'),
        array('id' => 3452342, 'title' => 'Some Title'),
        array('id' => 1231233, 'title' => 'Some Title'),
        array('id' => 5867867, 'title' => 'Some Title'),
    );

    $order = array_flip($order);
    $array = array_column($array,null,"id");
    $result = array_replace($order,$array);
    var_dump(array_values($result));

Mit doppelter id

<?php

    $order = array(3452342, 5867867, 7867867, 1231233);
    $array = array(
        array('id' => 7867867, 'title' => 'Some Title'),
        array('id' => 3452342, 'title' => 'Some Title'),
        array('id' => 1231233, 'title' => 'Some Title'),
        array('id' => 5867867, 'title' => 'Some Title'),
    );

    $order_dict = array_flip($order);
    $order_dict = array_combine($order,array_fill(0,count($order),[]));
    foreach($array as $item){
        $order_dict[$item["id"]][] = $item;
    }
    //$order_dict = array_filter($order_dict);  // if there is empty item on some id in $order array
    $result = [];
    foreach($order_dict as $items){
        foreach($items as $item){
            $result[] = $item;
        }
    }
    var_dump($result);
0
Kris Roofe