Come ordinare una matrice di array associativi in ​​base al valore di una determinata chiave in PHP?


446

Dato questo array:

$inventory = array(

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

);

Vorrei ordinare $inventorygli elementi per prezzo per ottenere:

$inventory = array(

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

);

Come posso fare questo?


Risposte:


608

Hai ragione, la funzione che stai cercando è array_multisort().

Ecco un esempio preso direttamente dal manuale e adattato al tuo caso:

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

A partire da PHP 5.5.0 puoi usare array_column () invece di quello foreach

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

array_multisort($price, SORT_DESC, $inventory);

5
Anche se questo è sicuramente più costoso delle alternative.
Matt,

5
Più costoso? È strano, sulla mia macchina (con PHP 5.3.1-dev) array_multisort () è un po 'più veloce su piccoli array e fino a 100 volte più veloce su grandi array (oltre 100 elementi)
Josh Davis,

3
Non dovrebbe richiedere alcuna modifica per funzionare con i tasti numerici. Se stai riscontrando un bug o un comportamento strano correlato ai tasti numerici, pubblicalo come nuova domanda.
Josh Davis,

4
array_multisort ha un grosso problema: non mantiene la chiave originale.
machineaddict,

1
@machineaddict mantiene le chiavi associative.
Matej Svajger,

318

PHP 7+

A partire da PHP 7, questo può essere fatto in modo conciso utilizzando usortuna funzione anonima che utilizza l' operatore di astronave per confrontare gli elementi.

Puoi fare un ordinamento crescente come questo:

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

O un ordinamento discendente come questo:

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

Per capire come funziona, nota che usortaccetta una funzione di confronto fornita dall'utente che deve comportarsi come segue (dai documenti):

La funzione di confronto deve restituire un numero intero minore, uguale o maggiore di zero se il primo argomento è considerato rispettivamente minore di, uguale a o maggiore del secondo.

E nota anche che <=>, l'operatore dell'astronave,

restituisce 0 se entrambi gli operandi sono uguali, 1 se la sinistra è maggiore e -1 se la destra è maggiore

che è esattamente ciò di cui ha usortbisogno. In effetti, quasi l'intera giustificazione fornita per l'aggiunta <=>alla lingua in https://wiki.php.net/rfc/combined-comparison-operator è che

usort()semplifica la scrittura di ordini callback da utilizzare con


PHP 5.3+

PHP 5.3 ha introdotto funzioni anonime, ma non ha ancora l'operatore di astronave. Possiamo ancora usare usortper ordinare il nostro array, ma è un po 'più dettagliato e difficile da capire:

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

Si noti che sebbene sia abbastanza comune per i comparatori che si occupano di valori interi restituire semplicemente la differenza dei valori, come $item2['price'] - $item1['price'], in questo caso non possiamo farlo in modo sicuro. Questo perché i prezzi sono numeri in virgola mobile nell'esempio di chi chiede la domanda, ma la funzione di confronto a cui passiamo usortdeve restituire numeri interi per usortfunzionare correttamente:

La restituzione di valori non interi dalla funzione di confronto, come float, comporterà un cast interno a un numero intero del valore restituito del callback. Quindi valori come 0,99 e 0,1 verranno entrambi espressi su un valore intero pari a 0, che confronterà tali valori come uguali.

Questa è una trappola importante da tenere a mente quando si usa usortin PHP 5.x! La mia versione originale di questa risposta ha fatto questo errore eppure ho accumulato dieci voti su migliaia di visualizzazioni apparentemente senza che nessuno si accorgesse del grave bug. La facilità con cui i deficienti come me possono rovinare le funzioni del comparatore è precisamente la ragione per cui l'operatore di astronave più facile da usare è stato aggiunto al linguaggio in PHP 7.


8
Siamo spiacenti, ma questo approccio elimina le chiavi di stringa dagli array associativi. Invece, dovrebbe essere usata la funzione "uasort".
Matteo-SoftNet,

8
@DotMat Interessante: non lo sapevo uasort. Dopo aver esaminato i documenti, tuttavia, questa risposta è ancora corretta in questo caso . Nell'esempio del PO, l'array da ordinare ha indici numerici sequenziali anziché indici di stringa, quindi usortè più appropriato. L'uso uasortsu una matrice indicizzata in sequenza comporterà una matrice ordinata che non è ordinata dai suoi indici numerici, in modo tale che il primo elemento visto in un foreachciclo non lo sia $your_array[0], il che è improbabile che sia un comportamento desiderabile.
Mark Amery,

100

Mentre altri hanno suggerito correttamente l'uso di array_multisort(), per qualche motivo nessuna risposta sembra riconoscere l'esistenza di array_column(), il che può semplificare notevolmente la soluzione. Quindi il mio suggerimento sarebbe:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);

1
Per qualche motivo non sono stato in grado di farlo funzionare con stringhe con lettere inferiori / superiori. Anche usando SORT_FLAG_CASE. Per me il confronto delle stringhe ha funzionato: array_multisort (array_map (strtolower, array_column ($ ipr_projects, 'Name')), SORT_ASC, $ ipr_projects);
Pabamato,

8
Questa è la risposta più elegante. Dovrebbe essere valutato molto più in alto!
Armin Hierstetter,

3
il più breve e semplice, a mio avviso accettato uno
StudioX,

1
Roba buona qui. Ha funzionato perfettamente per me!
Funk Doc,

Ha funzionato come un incantesimo, ty
Leif_Lundberg il

42

Poiché gli elementi dell'array sono array stessi con chiavi di stringa, la soluzione migliore è definire una funzione di confronto personalizzata. È abbastanza veloce e facile da fare. Prova questo:

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

Produce quanto segue:

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

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

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

)

4
Combinando con alcuni degli altri commenti qui (uasort e inline funzioni anonime), otterrai questo one-liner:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
Alan Porter

@AlanPorter usortsembra più appropriato rispetto uasortall'ordinamento di un array con tasti numerici sequenziali. Finire con un array in cui il primo elemento è all'indice 1e il secondo elemento all'indice 0è un comportamento strano e una trappola sicura per le persone che non hanno familiarità con i dettagli degli array di PHP; usortti dà l'output che ti aspetteresti intuitivamente.
Mark Amery,

25

Ho finito con questo:

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

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

Basta chiamare la funzione, passando l'array e il nome del campo dell'array di secondo livello. Piace:

sort_array_of_array($inventory, 'price');

1
Ha! Faccio praticamente esattamente la stessa cosa e stavo per pubblicare, ma ho visto il tuo ... votato.
Rob Evans,

1
Downvoting perché questa è esattamente la stessa soluzione che Josh Davis ha pubblicato anni prima.
Mark Amery,

Non sono d'accordo ... Non ho detto che è una soluzione diversa, ho solo detto che ho finito con questa soluzione e ho dato una funzione completa.
Danielzt,

1
@MarkAmery Preferisco le risposte contenute nelle funzioni. Incoraggia i copia-paster a utilizzare le funzioni e, si spera, scrivere meno codice spaghetti.
Goose,

19

È possibile utilizzare usortcon una funzione anonima, ad es

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });

Versioni PHP 5> = 5.5.0, PHP 7 per quelli come me che volevano davvero che questo funzionasse per loro ..
Matt P

1
Notevole che strnatcmp, pensato per confrontare le stringhe, sembra funzionare bene qui. Apparentemente, l '"ordine naturale" che implementa include l'ordinamento di stringhe numeriche numericamente piuttosto che lessicamente.
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");
}

uscite:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

6

Da Ordina un array di array associativi in ​​base al valore di una determinata chiave in php :

uasort ( http://php.net/uasort ) consente di ordinare un array con la propria funzione definita. Nel tuo caso, è semplice:

$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");

1
Questa risposta è arrivata nella coda di recensioni di bassa qualità, presumibilmente perché non fornisci alcuna spiegazione del codice. Se questo codice risponde alla domanda, considera di aggiungere un po 'di testo che spiega il codice nella tua risposta. In questo modo, hai molte più probabilità di ottenere più voti e aiutare l'interrogante a imparare qualcosa di nuovo.
lmo,

1
Hmpf. questa è la migliore risposta.
commonpike,

1
-1; la cmpfunzione qui è sbagliata. Dovrebbe restituire "un numero intero minore di, uguale a, o maggiore di zero se il primo argomento è considerato rispettivamente minore di, uguale a, o maggiore del secondo" ma invece restituisce trueo false. Sembra, comunque, che funzioni - forse perché l'attuale implementazione di usorte amici tratta i casi "minore di" e "uguale a" in modo identico - ma non contare sul fatto che continui a lavorare nelle future versioni di PHP. Se tentano di rendere stabili le specie (cioè non muoversi inutilmente attorno a elementi uguali), questo si romperà.
Mark Amery,

Inoltre, usortsarebbe più appropriato di uasortqui, poiché uasortpreserva l'associazione tra chiavi e valori che è confusa e inattesa quando deaing con una matrice numerica sequenziale. Ad esempio, gli indici $arraysopra riportati dopo la chiamata uasortsono 2, 0 e 1, in questo ordine. A meno che tu non voglia per qualche motivo, probabilmente ti sentirai più a tuo agio nell'utilizzare usort, il che reindicizza l'array e lo riordina.
Mark Amery,

5

È stato testato su 100000 record: tempo in secondi (calcolato dal microtime di funzione). Solo per valori univoci sull'ordinamento delle posizioni chiave.

Soluzione di funzione di @Josh Davis: tempo trascorso: 1.5768740177155

Miniera: tempo impiegato: 0,094044923782349

Soluzione:

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*
}

7
Tuttavia, il requisito per chiavi di ordinamento univoche è una specie di rompicapo. Se si hanno valori di ordinamento univoci che possono essere chiavi, si pone la domanda: perché non semplicemente costruire l'array con quelle chiavi per cominciare? Nello scenario del PO, è difficile immaginare che due articoli con lo stesso prezzo sarebbero impossibili . Ciò in mente, l'uso di questa soluzione farebbe scomparire misteriosamente e silenziosamente gli elementi dell'array dal set di risultati ordinato.
Chris Baker,

@ Chris Baker, hai ragione. Funziona solo con valori univoci. Ma questa soluzione funziona molto velocemente, quindi la velocità è stata la ragione per crearla e usarla. Al momento potrebbe non essere reale, è necessario testarlo con PHP 7.1.x.
Nefelim,

4

Uso uasortcosì

<?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);

3

Questa funzione è riutilizzabile:

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

Funziona bene sui valori di stringa per impostazione predefinita, ma dovrai sostituire il callback per una funzione di confronto dei numeri se tutti i tuoi valori sono numeri.


Lo chiami usortarrma poi chiami uasortinvece di usort; forse un po 'confuso. Quest'ultimo è - nel caso di un array sequenziale con indici numerici, come quello esposto nella domanda - probabilmente ciò che realmente vuoi.
Mark Amery,

2

Potresti provare a definire la tua funzione di confronto e quindi utilizzare usort .


Sì. Lo farò se non trovo una soluzione. Sono abbastanza sicuro che ci sono alcuni strani parametri che puoi aggiungere a uno dei tipi per ottenere questo risultato. Grazie per i tuoi pensieri però!
Matt,

2

Ecco un metodo che ho trovato molto tempo fa e ripulito un po '. Funziona benissimo e può essere rapidamente modificato per accettare anche oggetti.

/**
 * 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;

}

per cambiare il metodo per ordinare gli oggetti basta cambiare la seguente riga:

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

Per eseguire il metodo semplicemente

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


Questo approccio funziona, ma è un po 'meno conciso della risposta di Josh Davis (con array_multisort) o della mia (con usort) e non sembra offrire alcun vantaggio su di loro in cambio.
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
    )

)

1

prova questo:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);

Ciao e benvenuto su StackOverflow, e grazie per aver risposto. Mentre questo codice potrebbe rispondere alla domanda, puoi prendere in considerazione l'aggiunta di alcune spiegazioni per quale problema hai risolto e come lo hai risolto? Questo aiuterà i futuri lettori a capire meglio la tua risposta e ad imparare da essa.
Plutian,

0

Funzione dinamica completa Sono saltato qui per l'ordinamento di array associativo e ho trovato questa fantastica funzione su http://php.net/manual/en/function.sort.php . Questa funzione è molto dinamica che ordina in ordine crescente e decrescente con il tasto specificato.

Semplice funzione per ordinare un array in base a una chiave specifica. Mantiene l'associazione dell'indice

<?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

-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;


} 

Mentre questo frammento di codice può risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice. Cerca inoltre di non aggiungere il tuo codice a commenti esplicativi, in quanto ciò riduce la leggibilità sia del codice che delle spiegazioni!
Arrivederci StackExchange il

-5

prova questo:

asort($array_to_sort, SORT_NUMERIC);

per riferimento vedere questo: http://php.net/manual/en/function.asort.php

vedere qui vari flag di ordinamento: http://www.php.net/manual/en/function.sort.php


questo non funzionerà con array multidimensionali, ma mi ha aiutato a risolvere un altro problema, grazie :)
schellmax,

4
Questo non può essere usato per ordinare un elenco di dizionari in base a una particolare chiave del dizionario e quindi non risponde alla domanda posta.
Mark Amery,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.