İlişkili diziler dizisini PHP'de verilen bir anahtarın değerine göre nasıl sıralayabilirim?


446

Bu dizi verildiğinde:

$inventory = array(

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

);

Ben $inventory's öğeleri fiyat almak için sıralamak istiyorum :

$inventory = array(

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

);

Bunu nasıl yapabilirim?


Yanıtlar:


607

Haklısın, aradığın fonksiyon array_multisort().

Doğrudan kılavuzdan alınmış ve davanıza uyarlanmış bir örnek:

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

PHP 5.5.0'dan itibaren foreach yerine array_column () kullanabilirsiniz

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

array_multisort($price, SORT_DESC, $inventory);

5
Bu kesinlikle alternatiflerden daha pahalı olsa da.
Matt

5
Daha pahalı? Bu tuhaf, benim makinemde (PHP 5.3.1-dev çalıştıran) array_multisort () küçük dizilerde yüzde birkaç daha hızlı ve büyük dizilerde (100+ öğe) 100 kata kadar daha hızlı
Josh Davis

3
Sayısal tuşlarla çalışmak için herhangi bir değişiklik gerektirmemelidir. Sayısal tuşlarla ilgili bir hatayı veya garip bir davranışı vuruyorsanız, yeni bir soru olarak gönderin.
Josh Davis

4
array_multisort'un büyük bir sorunu vardır: orijinal anahtarı korumaz.
machineaddict

1
@machineaddict ilişkilendirilebilir anahtarları korur.
Matej Svajger

318

PHP 7+

PHP 7 itibaren bu kısaca kullanılarak yapılabilir usortbir ile anonim fonksiyonu kullanan uzay gemisi operatörü eleman karşılaştırmak.

Bunun gibi artan bir sıralama yapabilirsiniz:

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

Veya bunun gibi azalan bir sıralama:

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

Bunun nasıl çalıştığını anlamak için, usortaşağıdaki gibi davranması gereken kullanıcı tarafından sağlanan bir karşılaştırma işlevini aldığını unutmayın (dokümanlardan):

İlk bağımsız değişken, ikinciden sırasıyla küçük, eşit veya büyük olarak kabul edilirse karşılaştırma işlevi sıfırdan küçük, eşit veya sıfırdan büyük bir tamsayı döndürmelidir.

Ve ayrıca <=>, uzay gemisi operatörünün,

her iki işlenen eşitse 0, sol büyükse 1 ve sağ büyükse -1 döndürür

tam da neye usortihtiyacı var. Aslında, neredeyse eklemek için verilen tüm gerekçesi <=>de dile https://wiki.php.net/rfc/combined-comparison-operator öyle mi

ile kullanmak için yazma sipariş geri çağrıları usort()kolaylaştırır


PHP 5.3 ve üstü

PHP 5.3 anonim işlevler getirdi, ancak henüz uzay gemisi operatörü yok. usortDizimizi sıralamak için hala kullanabiliriz , ancak biraz daha ayrıntılı ve anlaşılması daha zor:

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

Not sadece değerlerin farkını dönmek için tamsayı değerleri ile uğraşan Karşılaştırıcıların için oldukça yaygın bir kullanımdır gibi o $item2['price'] - $item1['price'], biz olamaz güvenli bir biçimde yapmanın bu durumda. Bunun nedeni, fiyatların soru sorucunun örneğindeki kayan nokta sayılarıdır, ancak geçirdiğimiz karşılaştırma işlevinin düzgün çalışması usortiçin tamsayılar döndürmesi usortgerekir:

Geri dönen tamsayı olmayan örneğin kayan nokta olarak, karşılaştırma fonksiyonunun değerleri, geri dönüş değeri tam sayı için bir iç döküm neden olur. Dolayısıyla, 0,99 ve 0,1 gibi değerlerin her ikisi de, 0 gibi bir tamsayı değerine dönüştürülür ve bu değerler eşit olarak karşılaştırılır.

Bu usortPHP 5.x kullanırken akılda tutulması gereken önemli bir tuzaktır ! Bu cevabın orijinal versiyonum bu hatayı yaptı ve yine de ciddi bir hatayı fark etmeden görünüşe göre binlerce görüşe on upvot aldım. Beni karşılaştırıcı fonksiyonları berbat edebilir gibi kolaylığı hangi ile lackwits kesin kolay kullanımlı uzay gemisi operatörü PHP 7'de dile eklendiği sebep.


8
Maalesef, bu yaklaşım dize anahtarlarını ilişkilendirilebilir dizilerden siler. Bunun yerine "uasort" işlevi kullanılmalıdır.
Matteo-SoftNet

8
@DotMat İlginç - Bilmiyordum uasort. Bununla birlikte, dokümanlara baktıktan sonra , bu durumda bu cevap hala doğrudur . OP örneğinde, sıralanacak dizinin dize dizinleri yerine sıralı sayısal dizinleri vardır, bu usortnedenle daha uygundur. Kullanarak uasortbir sırayla endeksli dizi üzerinde bir görülen ilk elemanı bu şekilde kendi sayısal dizinler tarafından istenmemektedir sıralanmış bir dizi sonuçlanır foreachdöngü değildir $your_array[0]arzu davranış olması pek mümkün değildir.
Mark Amery

100

Diğerleri doğru bir şekilde kullanılmasını önermiş olsa da array_multisort(), nedense hiçbir cevap varlığını kabul etmemektedir ve array_column()bu da çözümü büyük ölçüde basitleştirebilir. Yani benim önerim:

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

1
Herhangi bir nedenle alt / üst harfli dizelerle çalışmasını sağlayamadım. SORT_FLAG_CASE kullanarak bile. Aşağıdakiler benim için dize karşılaştırması için çalıştı: array_multisort (array_map (strtolower, array_column ($ ipr_projects, 'Name')), SORT_ASC, $ ipr_projects);
Pabamato

8
Bu en zarif cevap. Çok daha yüksek derecelendirilmelidir!
Armin Hierstetter

3
en kısa ve en kolay olanı, bence kabul etti
StudioX

1
Burada iyi şeyler. Benim için mükemmel çalıştı!
Funk Doc

Bir cazibe gibi çalıştı, ty
Leif_Lundberg

42

Dizi öğeleriniz dize anahtarlarıyla dizildiğinden, en iyi seçeneğiniz özel bir karşılaştırma işlevi tanımlamaktır. Oldukça hızlı ve kolay. Bunu dene:

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

Aşağıdakileri üretir:

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

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

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

)

4
Buradaki diğer yorumlardan bazılarıyla (uasort ve satır içi anonim işlevler) birlikte, bu tek astarı elde edersiniz:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
Alan Porter

@AlanPorter , sıralı sayısal tuşlara sahip bir diziyi sıralamaktan usortdaha uygun görünmektedir uasort. İlk öğenin dizinde 1ve ikinci öğenin dizinde olduğu bir dizi ile bitirmek 0, garip davranış ve PHP dizilerinin ayrıntılarına aşina olmayan insanlar için kesin bir tuzaktır; usortsezgisel olarak beklediğiniz çıktıyı verir.
Mark Amery

25

Ben bununla bitirdim:

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

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

Diziyi ve ikinci düzey dizinin alanının adını ileterek işlevi çağırmanız yeterlidir. Sevmek:

sort_array_of_array($inventory, 'price');

1
Ha! Ben de hemen hemen aynı şeyi yapıyorum ve yayınlayacağım ama seninkini gördüm ...
Rob Evans

1
Downvoting çünkü bu Josh Davis'in yıllar önce yayınladığı çözümle tamamen aynı.
Mark Amery

Katılmıyorum ... Bunun farklı bir çözüm olduğunu söylemedim, sadece bu çözümle sonlandığımı ve tam bir çalışma fonksiyonu verdiğimi söyledim.
Danielzt

1
@MarkAmery Fonksiyonlarda bulunan cevapları tercih ederim. Kopyalama pastalarını işlevleri kullanmaya ve umarım daha az spagetti kodu yazmaya teşvik eder.
Kaz

19

usortAnonim işlevle kullanabilirsiniz , örn.

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

Sürümler PHP 5> = 5.5.0, benim gibi gerçekten onlar için çalışmak isteyenler için PHP 7 ..
Matt P

1
Dikkat çekici strnatcmp, dizeleri karşılaştırmak için, burada iyi çalışıyor gibi görünüyor. Görünüşe göre, uyguladığı "doğal düzen" sayısal dizeleri sözlük yerine sayısal olarak sıralamayı içerir.
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");
}

çıktılar:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

6

Gönderen php verilen anahtarın değeri ile birleşmeli diziler sırala dizisi :

uasort ( http://php.net/uasort ) bir diziyi kendi tanımladığınız işleve göre sıralamanızı sağlar. Sizin durumunuzda, bu basit:

$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
Bu yanıt, muhtemelen kod hakkında herhangi bir açıklama yapmadığınız için düşük kaliteli inceleme sırasında ortaya çıktı. Bu kod soruyu cevaplıyorsa, cevabınızdaki kodu açıklayan bir metin eklemeyi düşünün. Bu şekilde, daha fazla oy almanız ve ankete katılan kişinin yeni bir şey öğrenmesine yardımcı olmanız çok daha olasıdır.
lmo

1
hmpf. tho için en iyi cevap bu.
commonpike

1
1; buradaki cmpişlev yanlış. Eğer ilk argüman sırasıyla ikinciden daha küçük, ona eşit veya daha büyük olarak kabul edilirse "sıfırdan küçük, ona eşit veya sıfırdan büyük bir tamsayı" döndürmelidir , bunun yerine trueveya döndürür false. Yine de, belki de mevcut uygulama usortve arkadaşların "daha az" ve "eşit" vakalara aynı şekilde davrandığı için dikkat çekici bir şekilde görünüyor, ancak gelecekteki PHP sürümlerinde çalışmaya devam etmesine güvenmeyin. Eğer çeşitlerin kararlı olmasını sağlamaya çalışırlarsa (yani gereksiz elemanların etrafında gereksiz yere hareket etmiyorlarsa) bu kırılacaktır.
Mark Amery

Ayrıca, sıralı bir sayısal dizi ile işlem yaparken kafa karıştırıcı ve beklenmedik olan anahtarlar ve değerler arasındaki ilişkiyi koruduğundan, buradakinden usortdaha uygun olacaktır . Örneğin, çağrıldıktan sonra yukarıdaki dizinler bu sırayla 2, 0 ve 1'dir. Herhangi bir nedenden dolayı bunu istemediğiniz sürece , diziyi yeniden endeksleyen ve yeniden sıralayan muhtemelen daha rahat olacaksınız . uasortuasort$arrayuasortusort
Mark Amery

5

100.000 kayıtta test edilmiştir: Saniye cinsinden süre (funciton mikro zamanı ile hesaplanmıştır). Yalnızca anahtar konumlarını sıralamadaki benzersiz değerler için.

Josh Davis fonksiyonunun çözümü : Harcanan zaman : 1.5768740177155

Maden çözümü : Harcanan zaman : 0.094044923782349

Çözüm:

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
Benzersiz sıralama anahtarları için gereksinim bir tür anlaşma kırıcı olsa da. Anahtar olabilen benzersiz sıralama değerleriniz varsa, bu soruya yalvarır: neden diziyi bu tuşlarla başlamak için neden basitçe oluşturmuyorsunuz? OP senaryosunda, aynı fiyata sahip iki ürünün imkansız olacağını hayal etmek zor . Bu çözümü kullanmak, dizideki öğelerin gizemli ve sessizce sıralanan sonuç kümesinden kaybolmasına neden olur.
Chris Baker

@Chris Baker, haklısın. Bu yalnızca benzersiz değerler için geçerlidir. Ancak bu çözüm çok hızlı çalışır, bu yüzden hız yapmak ve kullanmak nedeniydi. Şu anda gerçek olmayabilir, PHP 7.1.x ile test etmeniz gerekiyor.
Nefelim

4

uasortBöyle kullanıyorum

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

Bu işlev yeniden kullanılabilir:

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

Varsayılan olarak dize değerlerinde iyi çalışır, ancak tüm değerleriniz sayıysa, bir sayı karşılaştırma işlevi için geri aramayı alt gruplara ayırmanız gerekir.


Bunu çağırırsınız usortarrama uasortyerine çağırırsınız usort; belki biraz kafa karıştırıcı. İkincisi - soruda gösterilen gibi sayısal indekslere sahip sıralı bir dizi durumunda - muhtemelen gerçekte ne istersiniz.
Mark Amery

2

Kendi karşılaştırma işlevinizi tanımlamayı ve ardından usort'u kullanmayı deneyebilirsiniz .


Evet. Bir çözüm bulamazsam bunu yaparım. Bunu başarmak için türlerinden birine ekleyebileceğiniz bazı garip parametreler olduğundan eminim. Düşünceleriniz için teşekkürler!
Matt

2

İşte uzun zaman önce bulduğum ve biraz temizlediğim bir yöntem. Bu harika çalışıyor ve nesneleri kabul etmek için hızlı bir şekilde değiştirilebilir.

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

}

nesneleri sıralama yöntemini değiştirmek için aşağıdaki satırı değiştirmeniz yeterlidir:

$tempArray[] = strtolower( $value[ $sortByKey ] ); için $tempArray[] = strtolower( $value->$sortByKey );

Yöntemi çalıştırmak için

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


Bu yaklaşım işe yarıyor, ama Josh Davis'in cevabından (ile array_multisort) veya benimkinden (ile usort) daha az özlü ve karşılığında onlara hiçbir avantaj sunmuyor gibi görünüyor.
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

bunu dene:

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

Merhaba ve stackoverflow'a hoş geldiniz ve yanıtladığınız için teşekkür ederiz. Bu kod soruyu cevaplayabilirken, sorunun ne çözdüğünüze ve nasıl çözdüğünüze ilişkin bir açıklama eklemeyi düşünebilir misiniz? Bu, gelecekteki okuyucuların cevabınızı daha iyi anlamalarına ve ondan öğrenmelerine yardımcı olacaktır.
Plutian

0

Tam Dinamik İşlev İlişkisel dizi sıralama için buraya atladım ve bu şaşırtıcı işlevi http://php.net/manual/en/function.sort.php adresinde buldum . Bu işlev, belirtilen tuşla artan ve azalan düzende sıralanan çok dinamiktir.

Bir diziyi belirli bir tuşa göre sıralamak için basit işlev. Dizin ilişkilendirmesini korur

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


} 

Bu kod snippet'i soruyu çözebilir, ancak bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olur. Gelecekte okuyucular için soruyu cevapladığınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceğini unutmayın. Lütfen hem kodun hem de açıklamaların okunabilirliğini azalttığı için kodunuzu açıklayıcı yorumlarla doldurmamaya çalışın!
Hoşçakal StackExchange

-5

bunu dene:

asort($array_to_sort, SORT_NUMERIC);

referans için bakınız: http://php.net/manual/en/function.asort.php

burada çeşitli sıralama bayraklarına bakın: http://www.php.net/manual/en/function.sort.php


bu çok boyutlu diziler için işe yaramayacak, ancak başka bir sorun için bana yardımcı oldu, teşekkürler :)
schellmax

4
Bu, sözlüklerin listesini belirli bir sözlük anahtarına göre sıralamak için kullanılamaz ve bu nedenle sorulan soruya cevap vermez.
Mark Amery
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.