Wie sortiere ich ein Array von assoziativen Arrays nach dem Wert eines bestimmten Schlüssels in PHP?

446

Angesichts dieses Arrays:

$inventory = array(

   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),
   array("type"=>"pork", "price"=>5.43),

);

Ich möchte die $inventoryElemente nach Preis sortieren , um Folgendes zu erhalten:

$inventory = array(

   array("type"=>"pork", "price"=>5.43),
   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),

);

Wie kann ich das machen?

Matt
quelle

Antworten:

605

Sie haben Recht, die Funktion, die Sie suchen, ist array_multisort().

Hier ist ein Beispiel, das direkt aus dem Handbuch stammt und an Ihren Fall angepasst ist:

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);

Ab PHP 5.5.0 können Sie array_column () anstelle von foreach verwenden

$price = array_column($inventory, 'price');

array_multisort($price, SORT_DESC, $inventory);
Josh Davis
quelle
5
Dies ist jedoch definitiv teurer als die Alternativen.
Matt
5
Teurer? Das ist seltsam, auf meinem Computer (mit PHP 5.3.1-dev) ist array_multisort () auf kleinen Arrays ein paar Prozent schneller und auf großen Arrays (über 100 Elemente) bis zu 100-mal schneller
Josh Davis
3
Es sollte keine Änderung erforderlich sein, um mit Zifferntasten zu arbeiten. Wenn Sie auf einen Fehler oder ein seltsames Verhalten im Zusammenhang mit Zifferntasten stoßen, stellen Sie ihn als neue Frage.
Josh Davis
4
array_multisort hat ein großes Problem: Der ursprüngliche Schlüssel wird nicht beibehalten.
Machineaddict
1
@machineaddict verwaltet die assoziativen Schlüssel.
Matej Svajger
316

PHP 7+

Ab PHP 7 kann dies usortmit einer anonymen Funktion , die den Raumschiffoperator verwendet, präzise erfolgen zum Vergleichen von Elementen , präzise erfolgen.

Sie können eine aufsteigende Sortierung wie folgt durchführen:

usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

Oder eine absteigende Sorte wie diese:

usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

Um zu verstehen, wie dies funktioniert, beachten Sie, dass usorteine vom Benutzer bereitgestellte Vergleichsfunktion verwendet wird, die sich wie folgt verhalten muss (aus den Dokumenten):

Die Vergleichsfunktion muss eine Ganzzahl zurückgeben, die kleiner, gleich oder größer als Null ist, wenn das erste Argument als kleiner, gleich oder größer als das zweite angesehen wird.

Und beachten Sie auch, dass <=>der Raumschiffbetreiber,

Gibt 0 zurück, wenn beide Operanden gleich sind, 1, wenn die linke größer ist, und -1, wenn die rechte größer ist

Welches ist genau das, was usortbraucht. Tatsächlich ist fast die gesamte Begründung für das Hinzufügen <=>zur Sprache in https://wiki.php.net/rfc/combined-comparison-operator, dass dies der Fall ist

usort()erleichtert das Schreiben von Bestellrückrufen zur Verwendung mit


PHP 5.3+

PHP 5.3 führte anonyme Funktionen ein, verfügt jedoch noch nicht über den Raumschiffoperator. Wir können usortunser Array immer noch sortieren, aber es ist etwas ausführlicher und schwerer zu verstehen:

usort($inventory, function ($item1, $item2) {
    if ($item1['price'] == $item2['price']) return 0;
    return $item1['price'] < $item2['price'] ? -1 : 1;
});

Beachten Sie, dass , obwohl es für Komparatoren ziemlich häufig ist mit ganzzahligen Werten zu tun nur die Differenz der Werte zurück, wie $item2['price'] - $item1['price']wir können nicht sicher tun , dass in diesem Fall. Dies liegt daran, dass die Preise im Beispiel des Fragestellers Gleitkommazahlen sind, aber die Vergleichsfunktion, an die wir übergeben, usortmuss Ganzzahlen zurückgeben, damit usortsie ordnungsgemäß funktioniert:

Das Zurückgeben von nicht ganzzahligen Werten aus der Vergleichsfunktion, z. B. float, führt zu einer internen Umwandlung in eine Ganzzahl des Rückgabewerts des Rückrufs. Werte wie 0,99 und 0,1 werden also beide auf einen ganzzahligen Wert von 0 umgewandelt, wodurch solche Werte als gleich verglichen werden.

Dies ist eine wichtige Falle, die Sie bei der Verwendung usortin PHP 5.x berücksichtigen sollten ! Meine ursprüngliche Version dieser Antwort hat diesen Fehler gemacht, und dennoch habe ich zehn positive Stimmen für Tausende von Ansichten gesammelt, ohne dass jemand den schwerwiegenden Fehler bemerkt hat. Die Leichtigkeit, mit der Schwachköpfe wie ich Komparatorfunktionen vermasseln können, ist genau der Grund, warum der benutzerfreundlichere Raumschiffoperator der Sprache in PHP 7 hinzugefügt wurde.

Mark Amery
quelle
8
Entschuldigung, aber dieser Ansatz löscht die Zeichenfolgenschlüssel aus assoziativen Arrays. Stattdessen sollte die Funktion "uasort" verwendet werden.
Matteo-SoftNet
8
@DotMat Interessant - ich wusste nichts darüber uasort. Nach dem Betrachten der Dokumente ist diese Antwort in diesem Fall jedoch immer noch korrekt . Im Beispiel des OP hat das zu sortierende Array sequentielle numerische Indizes anstelle von Zeichenfolgenindizes, usortwas besser geeignet ist. Die Verwendung uasorteines sequentiell indizierten Arrays führt zu einem sortierten Array, das nicht nach seinen numerischen Indizes geordnet ist, so dass das erste Element in einer foreachSchleife nicht geordnet ist $your_array[0], was wahrscheinlich kein wünschenswertes Verhalten ist.
Mark Amery
99

Während andere die Verwendung von richtig vorgeschlagen array_multisort()haben, scheint aus irgendeinem Grund keine Antwort die Existenz von anzuerkennen array_column(), was die Lösung erheblich vereinfachen kann. Mein Vorschlag wäre also:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);
Mariano Iglesias
quelle
1
Aus irgendeinem Grund konnte ich es nicht mit Zeichenfolgen mit unteren / oberen Buchstaben zum Laufen bringen. Auch mit der SORT_FLAG_CASE. Folgendes hat für den String-Vergleich für mich funktioniert: array_multisort (array_map (strtolower, array_column ($ ipr_projects, 'Name')), SORT_ASC, $ ipr_projects);
Pabamato
8
Dies ist die eleganteste Antwort. Sollte viel höher bewertet werden!
Armin Hierstetter
3
kürzeste und einfachste, akzeptierte eine meiner Meinung nach
StudioX
1
Gutes Zeug hier. Hat perfekt für mich funktioniert!
Funk Doc
Arbeitete wie ein Zauber, ty
Leif_Lundberg
42

Da Ihre Array-Elemente selbst Arrays mit String-Schlüsseln sind, ist es am besten, eine benutzerdefinierte Vergleichsfunktion zu definieren. Es ist ziemlich schnell und einfach zu machen. Versuche dies:

function invenDescSort($item1,$item2)
{
    if ($item1['price'] == $item2['price']) return 0;
    return ($item1['price'] < $item2['price']) ? 1 : -1;
}
usort($inventory,'invenDescSort');
print_r($inventory);

Produziert folgendes:

Array
(
    [0] => Array
        (
            [type] => pork
            [price] => 5.43
        )

    [1] => Array
        (
            [type] => fruit
            [price] => 3.5
        )

    [2] => Array
        (
            [type] => milk
            [price] => 2.9
        )

)
Zombat
quelle
4
In Kombination mit einigen anderen Kommentaren hier (uasort und inline anonyme Funktionen) erhalten Sie diesen Einzeiler:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
Alan Porter
@AlanPorter usortscheint geeigneter zu sein als uasortzum Sortieren eines Arrays mit fortlaufenden Zifferntasten. Das Ende eines Arrays, in dem sich das erste Element im Index 1und das zweite Element im Index 0befindet, ist ein seltsames Verhalten und eine sichere Falle für Personen, die mit den Details der PHP-Arrays nicht vertraut sind. usortgibt Ihnen die Ausgabe, die Sie intuitiv erwarten würden.
Mark Amery
25

Ich endete damit:

function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row)
    {
        $sortarray[$key] = $row[$subfield];
    }

    array_multisort($sortarray, SORT_ASC, $array);
}

Rufen Sie einfach die Funktion auf und übergeben Sie das Array und den Namen des Felds des Arrays der zweiten Ebene. Mögen:

sort_array_of_array($inventory, 'price');
Danielzt
quelle
1
Ha! Ich mache einfach so ziemlich genau das Gleiche und wollte etwas posten, habe aber gesehen, dass deins ... positiv bewertet wurde.
Rob Evans
1
Downvoting, weil dies genau die gleiche Lösung ist, die Josh Davis Jahre zuvor veröffentlicht hat.
Mark Amery
Nicht einverstanden ... Ich habe nicht gesagt, dass es eine andere Lösung ist, ich habe nur gesagt, dass ich mit dieser Lösung geendet habe und eine voll funktionsfähige Funktion gebe.
Danielzt
1
@ MarkAmery Ich bevorzuge Antworten in Funktionen. Es ermutigt Kopierpaster, Funktionen zu verwenden und hoffentlich weniger Spaghetti-Code zu schreiben.
Gans
19

Sie können usortmit anonymer Funktion verwenden, z

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });
Kenorb
quelle
Versionen PHP 5> = 5.5.0, PHP 7 für diejenigen unter Ihnen wie mich, die wirklich wollten, dass dies für sie funktioniert.
Matt P
1
Bemerkenswert, dass hier strnatcmp, um Strings zu vergleichen, gut zu funktionieren scheint. Anscheinend beinhaltet die "natürliche Reihenfolge", die sie implementiert, das Sortieren numerischer Zeichenfolgen numerisch und nicht lexikalisch.
Mark Amery
10
$inventory = 
    array(array("type"=>"fruit", "price"=>3.50),
          array("type"=>"milk", "price"=>2.90),
          array("type"=>"pork", "price"=>5.43),
          );

function pricesort($a, $b) {
  $a = $a['price'];
  $b = $b['price'];
  if ($a == $b)
    return 0;
  return ($a > $b) ? -1 : 1;
}

usort($inventory, "pricesort");
// uksort($inventory, "pricesort");

print("first: ".$inventory[0]['type']."\n\n");
// for usort(): prints milk (item with lowest price)
// for uksort(): prints fruit (item with key 0 in the original $inventory)

// foreach prints the same for usort and uksort.
foreach($inventory as $i){
  print($i['type'].": ".$i['price']."\n");
}

Ausgänge:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9
Danamlund
quelle
6

Von Sortieren Sie ein Array von assoziativen Arrays nach dem Wert des angegebenen Schlüssels in PHP :

Mit uasort ( http://php.net/uasort ) können Sie ein Array nach Ihrer eigenen definierten Funktion sortieren. In Ihrem Fall ist das einfach:

$array = array(
  array('price'=>'1000.50','product'=>'test1'),
  array('price'=>'8800.50','product'=>'test2'),
  array('price'=>'200.0','product'=>'test3')
);

function cmp($a, $b) {
  return $a['price'] > $b['price'];
}

uasort($array, "cmp");
Kamal
quelle
1
Diese Antwort wurde in der Überprüfungswarteschlange mit geringer Qualität angezeigt, vermutlich weil Sie keine Erklärung für den Code angeben. Wenn dieser Code die Frage beantwortet, sollten Sie Text hinzufügen, der den Code in Ihrer Antwort erläutert. Auf diese Weise erhalten Sie mit größerer Wahrscheinlichkeit mehr positive Stimmen - und helfen dem Fragesteller, etwas Neues zu lernen.
lmo
1
hmpf. Dies ist die beste Antwort.
Commonpike
1
-1; Die cmpFunktion hier ist falsch. Es soll "eine ganze Zahl kleiner, gleich oder größer als Null zurückgeben, wenn das erste Argument als kleiner, gleich oder größer als das zweite angesehen wird", aber stattdessen trueoder zurückgeben false. Es scheint bemerkenswerterweise dennoch zu funktionieren - vielleicht weil die aktuelle Implementierung von usortund friends die Fälle "kleiner als" und "gleich" identisch behandelt -, aber rechnen Sie nicht damit, dass es in zukünftigen PHP-Versionen weiter funktioniert. Wenn sie versuchen, Sortierungen stabil zu machen (dh sich nicht unnötig um gleiche Elemente zu bewegen), wird dies unterbrochen.
Mark Amery
Auch usortwäre besser geeignet als uasorthier, da uasortbewahrt die Zuordnung zwischen Schlüsseln und Werten , die verwirrend und unerwartet , wenn sie mit einem sequentiellen numerischen Array deaing. Zum Beispiel sind die oben genannten Indizes $arraynach dem Aufruf uasort2, 0 und 1 in dieser Reihenfolge. Wenn Sie dies nicht aus irgendeinem Grund wünschen, werden Sie wahrscheinlich komfortabler damit usortumgehen können, wodurch das Array neu indiziert und neu angeordnet wird.
Mark Amery
5

Wurde an 100 000 Datensätzen getestet: Zeit in Sekunden (berechnet durch Funktionsmikrozeit). Nur für eindeutige Werte beim Sortieren von Schlüsselpositionen.

Funktionslösung von @Josh Davis: Zeitaufwand : 1.5768740177155

Minenlösung : Verbringen Sie Zeit : 0,094044923782349

Lösung:

function SortByKeyValue($data, $sortKey, $sort_flags=SORT_ASC)
{
    if (empty($data) or empty($sortKey)) return $data;

    $ordered = array();
    foreach ($data as $key => $value)
        $ordered[$value[$sortKey]] = $value;

    ksort($ordered, $sort_flags);

    return array_values($ordered); *// array_values() added for identical result with multisort*
}
Nefelim
quelle
7
Die Anforderung an eindeutige Sortierschlüssel ist jedoch eine Art Deal Breaker. Wenn Sie eindeutige Sortierwerte haben, die Schlüssel sein können, stellt sich die Frage: Warum nicht einfach das Array mit diesen Schlüsseln erstellen? Im OP-Szenario ist es schwer vorstellbar, dass zwei Artikel mit demselben Preis unmöglich wären . Wenn Sie diese Lösung verwenden, werden Elemente aus dem Array auf mysteriöse Weise und stillschweigend aus der sortierten Ergebnismenge entfernt.
Chris Baker
@ Chris Baker, du hast recht. Dies funktioniert nur für eindeutige Werte. Diese Lösung funktioniert jedoch sehr schnell, daher war Geschwindigkeit der Grund für die Erstellung und Verwendung. Im Moment kann es sein, dass es nicht aktuell ist, muss es mit PHP 7.1.x getestet werden.
Nefelim
4

Ich benutze uasortso

<?php
$users = [
    [
        'username' => 'joe',
        'age' => 11
    ],
    [
        'username' => 'rakoto',
        'age' => 21
    ],
    [
        'username' => 'rabe',
        'age' => 17
    ],
    [
        'username' => 'fy',
        'age' => 19
    ],    
];


uasort($users, function ($item, $compare) {
    return $item['username'] >= $compare['username']; 
});

var_dump($users);
Mirado
quelle
3

Diese Funktion ist wiederverwendbar:

function usortarr(&$array, $key, $callback = 'strnatcasecmp') {
    uasort($array, function($a, $b) use($key, $callback) {
        return call_user_func($callback, $a[$key], $b[$key]);
    });
}

Es funktioniert gut auf String - Werte standardmäßig, aber Sie werden für eine zu sub den Rückruf haben Nummer Vergleichsfunktion , wenn alle Werte Zahlen sind.

mpen
quelle
Sie nennen dies usortarr, rufen dann aber uasortstatt an usort; vielleicht ein bisschen verwirrend. Letzteres ist - im Fall eines sequentiellen Arrays mit numerischen Indizes, wie das in der Frage gezeigte - wahrscheinlich das, was Sie tatsächlich wollen.
Mark Amery
2

Sie könnten versuchen, Ihre eigene Vergleichsfunktion zu definieren und dann usort zu verwenden .

Alex Sexton
quelle
Ja. Ich mache das, wenn ich keine Lösung finde. Ich bin mir ziemlich sicher, dass es einige seltsame Parameter gibt, die Sie zu einer der Sorten hinzufügen können, um dies zu erreichen. Vielen Dank für Ihre Gedanken!
Matt
2

Hier ist eine Methode, die ich vor langer Zeit gefunden und ein bisschen aufgeräumt habe. Dies funktioniert hervorragend und kann schnell geändert werden, um auch Objekte zu akzeptieren.

/**
 * A method for sorting arrays by a certain key:value.
 * SortByKey is the key you wish to sort by
 * Direction can be ASC or DESC.
 *
 * @param $array
 * @param $sortByKey
 * @param $sortDirection
 * @return array
 */
private function sortArray($array, $sortByKey, $sortDirection) {

    $sortArray = array();
    $tempArray = array();

    foreach ( $array as $key => $value ) {
        $tempArray[] = strtolower( $value[ $sortByKey ] );
    }

    if($sortDirection=='ASC'){ asort($tempArray ); }
        else{ arsort($tempArray ); }

    foreach ( $tempArray as $key => $temp ){
        $sortArray[] = $array[ $key ];
    }

    return $sortArray;

}

Um die Methode zum Sortieren von Objekten zu ändern, ändern Sie einfach die folgende Zeile:

$tempArray[] = strtolower( $value[ $sortByKey ] ); zu $tempArray[] = strtolower( $value->$sortByKey );

Um die Methode auszuführen, tun Sie einfach

sortArray($inventory,'price','ASC');

lzoesch
quelle
Dieser Ansatz funktioniert, ist aber ein bisschen weniger prägnant als die Antwort von Josh Davis (mit array_multisort) oder meine (mit usort) und scheint im Gegenzug keine Vorteile gegenüber ihnen zu bieten.
Mark Amery
1
//Just in one line custom function
function cmp($a, $b)
{
return (float) $a['price'] < (float)$b['price'];
}
@uasort($inventory, "cmp");
print_r($inventory);

//result

Array
(
[2] => Array
    (
        [type] => pork
        [price] => 5.43
    )

[0] => Array
    (
        [type] => fruit
        [price] => 3.5
    )

[1] => Array
    (
        [type] => milk
        [price] => 2.9
    )

)
Kamal
quelle
1

Versuche dies:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);
Jamshid
quelle
Hallo und willkommen bei stackoverflow, und danke für die Antwort. Während dieser Code möglicherweise die Frage beantwortet, können Sie eine Erklärung hinzufügen, was das Problem ist, das Sie gelöst haben, und wie Sie es gelöst haben? Dies wird zukünftigen Lesern helfen, Ihre Antwort besser zu verstehen und daraus zu lernen.
Plutian
0

Vollständige dynamische Funktion Ich bin hier zur assoziativen Array-Sortierung gesprungen und habe diese erstaunliche Funktion auf http://php.net/manual/en/function.sort.php gefunden . Diese Funktion ist sehr dynamisch und sortiert in aufsteigender und absteigender Reihenfolge mit dem angegebenen Schlüssel.

Einfache Funktion zum Sortieren eines Arrays nach einem bestimmten Schlüssel. Behält die Indexzuordnung bei

<?php

function array_sort($array, $on, $order=SORT_ASC)
{
    $new_array = array();
    $sortable_array = array();

    if (count($array) > 0) {
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                foreach ($v as $k2 => $v2) {
                    if ($k2 == $on) {
                        $sortable_array[$k] = $v2;
                    }
                }
            } else {
                $sortable_array[$k] = $v;
            }
        }

        switch ($order) {
            case SORT_ASC:
                asort($sortable_array);
            break;
            case SORT_DESC:
                arsort($sortable_array);
            break;
        }

        foreach ($sortable_array as $k => $v) {
            $new_array[$k] = $array[$k];
        }
    }

    return $new_array;
}

$people = array(
    12345 => array(
        'id' => 12345,
        'first_name' => 'Joe',
        'surname' => 'Bloggs',
        'age' => 23,
        'sex' => 'm'
    ),
    12346 => array(
        'id' => 12346,
        'first_name' => 'Adam',
        'surname' => 'Smith',
        'age' => 18,
        'sex' => 'm'
    ),
    12347 => array(
        'id' => 12347,
        'first_name' => 'Amy',
        'surname' => 'Jones',
        'age' => 21,
        'sex' => 'f'
    )
);

print_r(array_sort($people, 'age', SORT_DESC)); // Sort by oldest first
print_r(array_sort($people, 'surname', SORT_ASC)); // Sort by surname
Ahmad Sayeed
quelle
-1
$arr1 = array(

    array('id'=>1,'name'=>'aA','cat'=>'cc'),
    array('id'=>2,'name'=>'aa','cat'=>'dd'),
    array('id'=>3,'name'=>'bb','cat'=>'cc'),
    array('id'=>4,'name'=>'bb','cat'=>'dd')
);

$result1 = array_msort($arr1, array('name'=>SORT_DESC);

$result2 = array_msort($arr1, array('cat'=>SORT_ASC);

$result3 = array_msort($arr1, array('name'=>SORT_DESC, 'cat'=>SORT_ASC));


function array_msort($array, $cols)
{
    $colarr = array();
    foreach ($cols as $col => $order) {
    $colarr[$col] = array();
    foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
}

$eval = 'array_multisort(';

foreach ($cols as $col => $order) {
    $eval .= '$colarr[\''.$col.'\'],'.$order.',';
}

$eval = substr($eval,0,-1).');';
eval($eval);
$ret = array();
foreach ($colarr as $col => $arr) {
    foreach ($arr as $k => $v) {
        $k = substr($k,1);
        if (!isset($ret[$k])) $ret[$k] = $array[$k];
        $ret[$k][$col] = $array[$k][$col];
    }
}
return $ret;


} 
Chirag Pipariya
quelle
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen. Bitte versuchen Sie auch, Ihren Code nicht mit erklärenden Kommentaren zu überfüllen, da dies die Lesbarkeit sowohl des Codes als auch der Erklärungen beeinträchtigt!
Auf Wiedersehen StackExchange
-5

Versuche dies:

asort($array_to_sort, SORT_NUMERIC);

Referenz finden Sie hier: http://php.net/manual/en/function.asort.php

Hier finden Sie verschiedene Sortierflags: http://www.php.net/manual/en/function.sort.php

Sarsnake
quelle
Dies funktioniert nicht für mehrdimensionale Arrays, hat mir aber nur bei einem anderen Problem geholfen, danke :)
schellmax
4
Dies kann nicht verwendet werden, um eine Liste von Wörterbüchern nach einem bestimmten Wörterbuchschlüssel zu sortieren, und beantwortet daher nicht die gestellte Frage.
Mark Amery