Bagaimana cara mengurutkan array array asosiatif dengan nilai kunci yang diberikan dalam PHP?

446

Diberikan array ini:

$inventory = array(

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

);

Saya ingin mengurutkan $inventoryelemen berdasarkan harga untuk mendapatkan:

$inventory = array(

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

);

Bagaimana saya bisa melakukan ini?

Mat
sumber

Jawaban:

605

Anda benar, fungsi yang Anda cari adalah array_multisort() .

Berikut ini contoh yang diambil langsung dari manual dan disesuaikan dengan kasus Anda:

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

Pada PHP 5.5.0 Anda dapat menggunakan array_column () bukan foreach itu

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

array_multisort($price, SORT_DESC, $inventory);
Josh Davis
sumber
5
Padahal ini jelas lebih mahal daripada alternatifnya.
Matt
5
Lebih mahal? Itu aneh, di komputer saya (menjalankan PHP 5.3.1-dev) array_multisort () adalah beberapa persen lebih cepat pada array kecil dan sampai 100 kali lebih cepat pada array besar (100 + elemen)
Josh Davis
3
Seharusnya tidak memerlukan perubahan apa pun untuk bekerja dengan kunci numerik. Jika Anda menabrak bug atau perilaku aneh yang terkait dengan kunci numerik, poskan itu sebagai pertanyaan baru.
Josh Davis
4
array_multisort memiliki masalah besar: tidak mempertahankan kunci asli.
machineaddict
1
@machineaddict mempertahankan kunci asosiatif.
Matej Svajger
318

PHP 7+

Pada PHP 7, ini dapat dilakukan secara ringkas menggunakan usortdengan fungsi anonim yang menggunakan operator pesawat ruang angkasa untuk membandingkan elemen.

Anda dapat melakukan semacam naik seperti ini:

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

Atau jenis menurun seperti ini:

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

Untuk memahami cara kerjanya, perhatikan bahwa usortmengambil fungsi perbandingan yang disediakan pengguna yang harus berperilaku sebagai berikut (dari dokumen):

Fungsi perbandingan harus mengembalikan bilangan bulat kurang dari, sama dengan, atau lebih besar dari nol jika argumen pertama dianggap masing-masing kurang dari, sama dengan, atau lebih besar dari yang kedua.

Dan perhatikan juga bahwa <=>, operator pesawat ruang angkasa,

mengembalikan 0 jika kedua operan sama, 1 jika kiri lebih besar, dan -1 jika kanan lebih besar

yang persis apa yang usortdibutuhkan. Bahkan, hampir seluruh justifikasi yang diberikan untuk menambahkan <=>bahasa di https://wiki.php.net/rfc/combined-comparison-operator adalah bahwa ia

membuat penulisan callback pemesanan untuk digunakan dengan usort()lebih mudah


PHP 5.3+

PHP 5.3 memperkenalkan fungsi anonim, tetapi belum memiliki operator pesawat ruang angkasa. Kita masih bisa menggunakan usortuntuk mengurutkan array kita, tetapi sedikit lebih verbose dan lebih sulit untuk dipahami:

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

Perhatikan bahwa meskipun cukup umum untuk pembanding yang berurusan dengan nilai integer untuk hanya mengembalikan perbedaan nilai, seperti $item2['price'] - $item1['price'], kami tidak dapat melakukannya dengan aman dalam kasus ini. Ini karena harga adalah angka floating point dalam contoh penanya pertanyaan, tetapi fungsi perbandingan yang kami berikan usortharus mengembalikan bilangan bulat usortagar berfungsi dengan benar:

Mengembalikan nilai non-integer dari fungsi perbandingan, seperti float, akan menghasilkan gips internal ke integer dari nilai pengembalian callback. Jadi nilai-nilai seperti 0,99 dan 0,1 keduanya akan dilemparkan ke nilai integer 0, yang akan membandingkan nilai-nilai tersebut sebagai sama.

Ini adalah jebakan penting yang harus diingat ketika menggunakan usortdi PHP 5.x! Versi asli saya dari jawaban ini membuat kesalahan ini, namun saya mendapatkan sepuluh upvotes lebih dari ribuan tampilan rupanya tanpa ada yang memperhatikan bug serius. Kemudahan yang lackwits seperti saya dapat mengacaukan fungsi komparator adalah justru alasan bahwa operator pesawat ruang angkasa lebih mudah digunakan telah ditambahkan ke bahasa dalam PHP 7.

Mark Amery
sumber
8
Maaf, tetapi pendekatan ini menghapus kunci string dari array asosiatif. Fungsi "uasort" harus digunakan.
Matteo-SoftNet
8
@ DoMat Menarik - Saya tidak tahu tentang uasort. Namun, setelah melihat dokumen, jawaban ini masih benar dalam kasus ini . Dalam contoh OP, array yang akan diurutkan memiliki indeks numerik berurutan daripada indeks string, jadi usortlebih tepat. Menggunakan uasortpada array berindeks berurutan akan menghasilkan array yang diurutkan yang tidak dipesan oleh indeks numeriknya, sehingga elemen pertama yang terlihat dalam satu foreachlingkaran tidak $your_array[0], yang tidak mungkin merupakan perilaku yang diinginkan.
Mark Amery
100

Sementara yang lain dengan tepat menyarankan penggunaan array_multisort(), untuk beberapa alasan tidak ada jawaban yang mengakui keberadaan array_column(), yang dapat sangat menyederhanakan solusi. Jadi saran saya adalah:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);
Mariano Iglesias
sumber
1
Untuk beberapa alasan saya tidak dapat membuatnya bekerja dengan string yang memiliki huruf kecil / atas. Bahkan menggunakan SORT_FLAG_CASE. Berikut ini berfungsi untuk perbandingan string untuk saya: array_multisort (array_map (strtolower, array_column ($ ipr_projects, 'Name')), SORT_ASC, $ ipr_projects);
Pabamato
8
Ini jawaban yang paling elegan. Harus dinilai jauh lebih tinggi!
Armin Hierstetter
3
yang terpendek dan termudah, diterima menurut saya
StudioX
1
Barang bagus di sini. Bekerja dengan sempurna untuk saya!
Funk Doc
Bekerja seperti pesona, ty
Leif_Lundberg
42

Karena elemen array Anda adalah array sendiri dengan kunci string, taruhan terbaik Anda adalah dengan menentukan fungsi perbandingan kustom. Ini cukup cepat dan mudah dilakukan. Coba ini:

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

Menghasilkan yang berikut:

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

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

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

)
zombat
sumber
4
Menggabungkan dengan beberapa komentar lain di sini (uasort dan fungsi anonim sebaris), Anda mendapatkan satu baris ini:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
Alan Porter
@AlanPorter usorttampaknya lebih tepat daripada uasortmenyortir array dengan kunci numerik berurutan. Mengakhiri array di mana elemen pertama berada di indeks 1dan elemen kedua di indeks 0adalah perilaku aneh dan perangkap yang pasti bagi orang-orang yang tidak terbiasa dengan rincian array PHP; usortmemberi Anda output yang Anda harapkan secara intuitif.
Mark Amery
25

Saya mengakhiri ini:

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

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

Panggil saja fungsinya, meneruskan larik dan nama bidang larik tingkat kedua. Suka:

sort_array_of_array($inventory, 'price');
Danielzt
sumber
1
Ha! Saya hanya membuat hampir persis hal yang sama dan akan memposting tetapi melihat milik Anda ... terbalik.
Rob Evans
1
Menunduk karena ini adalah solusi yang persis sama dengan yang diposkan Josh Davis bertahun-tahun sebelumnya.
Mark Amery
Tidak setuju ... Saya tidak mengatakan itu solusi yang berbeda, saya hanya mengatakan bahwa saya mengakhiri dengan solusi ini dan memberikan fungsi kerja penuh.
Danielzt
1
@MarkAmery Saya lebih suka jawaban yang terkandung dalam fungsi. Ini mendorong penyalin menyalin untuk menggunakan fungsi dan mudah-mudahan menulis lebih sedikit kode spaghetti.
Angsa
19

Anda dapat menggunakan usortdengan fungsi anonim, mis

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });
kenorb
sumber
Versi PHP 5> = 5.5.0, PHP 7 bagi Anda seperti saya yang benar-benar ingin ini bekerja untuk mereka ..
Matt P
1
Luar biasa itu strnatcmp, dimaksudkan untuk membandingkan string, tampaknya berfungsi dengan baik di sini. Rupanya "tatanan alam" yang diterapkannya mencakup penyortiran string numerik secara numerik daripada leksikal.
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");
}

output:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9
danamlund
sumber
6

Dari Urutkan array array asosiatif dengan nilai kunci yang diberikan dalam php :

uasort ( http://php.net/uasort ) memungkinkan Anda untuk mengurutkan array berdasarkan fungsi yang Anda tentukan sendiri. Dalam kasus Anda, itu sederhana:

$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
sumber
1
Jawaban ini muncul dalam antrian ulasan berkualitas rendah, mungkin karena Anda tidak memberikan penjelasan kode apa pun. Jika kode ini menjawab pertanyaan, pertimbangkan untuk menambahkan beberapa teks yang menjelaskan kode dalam jawaban Anda. Dengan cara ini, Anda jauh lebih mungkin untuk mendapatkan lebih banyak upvotes - dan membantu si penanya mempelajari sesuatu yang baru.
lmo
1
hmpf. inilah jawaban terbaiknya.
commonpike
1
-1; yang cmpfungsi di sini adalah salah. Seharusnya mengembalikan "integer kurang dari, sama dengan, atau lebih besar dari nol jika argumen pertama dianggap masing-masing kurang dari, sama dengan, atau lebih besar dari yang kedua" tetapi sebaliknya mengembalikan trueatau false. Tampaknya, sangat, untuk pekerjaan - mungkin karena implementasi saat ini usortdan teman memperlakukan "kurang dari" dan "sama dengan" kasus identik - tetapi tidak mengandalkan itu melanjutkan bekerja di versi PHP masa depan. Jika mereka mencoba membuat sort menjadi stabil (yaitu tidak bergerak dengan elemen yang sama tidak perlu), ini akan pecah.
Mark Amery
Juga, usortakan lebih tepat daripada di uasortsini, karena uasortmempertahankan hubungan antara kunci dan nilai-nilai yang membingungkan dan tak terduga ketika berkerja dengan array numerik berurutan. Misalnya, indeks di $arrayatas setelah panggilan uasortadalah 2, 0, dan 1, dalam urutan itu. Kecuali Anda karena suatu alasan menginginkannya, Anda mungkin akan lebih nyaman menggunakan usort, yang mengindeks ulang array serta menata ulang itu.
Mark Amery
5

Diuji pada 100.000 catatan: Waktu dalam detik (dihitung dengan funciton microtime). Hanya untuk nilai unik pada penyortiran posisi kunci.

Solusi fungsi @Josh Davis: Waktu yang dihabiskan: 1.5768740177155

Solusi tambang: Waktu yang dihabiskan: 0,094044923782349

Larutan:

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
sumber
7
Namun, persyaratan untuk kunci pengurutan yang unik adalah semacam pemecah kesepakatan. Jika Anda memiliki nilai pengurutan unik yang bisa menjadi kunci, itu memunculkan pertanyaan: mengapa tidak langsung membuat array dengan kunci-kunci itu? Dalam skenario OP, sulit untuk membayangkan bahwa dua item dengan harga yang sama tidak mungkin . Itu dalam pikiran, menggunakan solusi ini akan menyebabkan item dari array menghilang secara misterius dan diam-diam dari set hasil yang diurutkan.
Chris Baker
@ Chris Baker, Anda benar. Ini hanya berfungsi untuk nilai unik. Tetapi solusi ini bekerja sangat cepat, jadi kecepatan adalah alasan untuk membuat dan menggunakannya. Saat ini mungkin itu tidak aktual, perlu mengujinya dengan PHP 7.1.x.
Nefelim
4

Saya menggunakan uasortseperti ini

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

Fungsi ini dapat digunakan kembali:

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

Ini berfungsi dengan baik pada nilai string secara default, tetapi Anda harus melakukan panggilan balik untuk fungsi perbandingan angka jika semua nilai Anda adalah angka.

Mpen
sumber
Anda memanggil ini usortarrtetapi kemudian menelepon uasortbukannya usort; mungkin agak membingungkan. Yang terakhir adalah - dalam kasus array berurutan dengan indeks numerik, seperti yang diperlihatkan dalam pertanyaan - mungkin apa yang sebenarnya Anda inginkan.
Mark Amery
2

Anda mungkin mencoba mendefinisikan fungsi perbandingan Anda sendiri dan kemudian menggunakan usort .

Alex Sexton
sumber
Iya. Saya akan melakukannya jika saya tidak dapat menemukan solusi. Saya cukup yakin ada beberapa parameter aneh yang dapat Anda tambahkan ke salah satu dari beberapa hal untuk mencapai ini. Terima kasih atas pemikiran Anda!
Matt
2

Berikut adalah metode yang saya temukan sejak lama dan sedikit dibersihkan. Ini berfungsi dengan baik, dan dapat dengan cepat diubah untuk menerima objek juga.

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

}

untuk mengubah metode untuk mengurutkan objek cukup ubah baris berikut:

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

Untuk menjalankan metode ini cukup lakukan

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

lzoesch
sumber
Pendekatan ini bekerja, tetapi agak kurang ringkas daripada jawaban Josh Davis (dengan array_multisort) atau punyaku (dengan usort) dan tampaknya tidak menawarkan keuntungan dibandingkan dengan mereka sebagai gantinya.
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
sumber
1

coba ini:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);
jamshid
sumber
Hai dan selamat datang di stackoverflow, dan terima kasih telah menjawab. Meskipun kode ini dapat menjawab pertanyaan, dapatkah Anda mempertimbangkan untuk menambahkan beberapa penjelasan untuk masalah apa yang Anda selesaikan, dan bagaimana Anda menyelesaikannya? Ini akan membantu pembaca di masa depan untuk memahami jawaban Anda dengan lebih baik dan belajar darinya.
Plutian
0

Lengkap Fungsi Dinamis Saya melompat di sini untuk mengurutkan array asosiatif dan menemukan fungsi luar biasa ini di http://php.net/manual/en/function.sort.php . Fungsi ini sangat dinamis yang mengurutkan dalam urutan naik dan turun dengan kunci yang ditentukan.

Fungsi sederhana untuk mengurutkan array dengan kunci tertentu. Mempertahankan hubungan indeks

<?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
sumber
-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
sumber
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda. Tolong juga cobalah untuk tidak membuat kerumunan kode Anda dengan komentar penjelasan, karena ini mengurangi keterbacaan kode dan penjelasan!
Selamat tinggal StackExchange
-5

coba ini:

asort($array_to_sort, SORT_NUMERIC);

untuk referensi lihat ini: http://php.net/manual/en/function.asort.php

lihat berbagai macam sortir di sini: http://www.php.net/manual/en/function.sort.php

pembuatan sampul
sumber
ini tidak akan berfungsi untuk array multidimensi, tetapi hanya membantu saya keluar untuk masalah lain, terima kasih :)
schellmax
4
Ini tidak dapat digunakan untuk mengurutkan daftar kamus dengan kunci kamus tertentu, dan karenanya tidak menjawab pertanyaan yang diajukan.
Mark Amery