Как отсортировать массив ассоциативных массивов по значению заданного ключа в PHP?

446

Учитывая этот массив:

$inventory = array(

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

);

Я хотел бы отсортировать $inventoryэлементы по цене, чтобы получить:

$inventory = array(

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

);

Как я могу это сделать?

Matt
источник

Ответы:

608

Вы правы, функция, которую вы ищете, есть array_multisort().

Вот пример, взятый прямо из руководства и адаптированный к вашему случаю:

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

В PHP 5.5.0 вы можете использовать array_column () вместо этого foreach

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

array_multisort($price, SORT_DESC, $inventory);
Джош Дэвис
источник
5
Хотя это определенно дороже, чем альтернативы.
Мэтт
5
Более дорогой? Это странно, на моей машине (с PHP 5.3.1-dev) array_multisort () на несколько процентов быстрее для маленьких массивов и до 100 раз быстрее для больших массивов (более 100 элементов)
Josh Davis
3
Для работы с цифровыми клавишами не требуется никаких изменений. Если вы столкнулись с ошибкой или странным поведением, связанным с цифровыми клавишами, опубликуйте его как новый вопрос.
Джош Дэвис
4
У array_multisort есть большая проблема: он не поддерживает исходный ключ.
Machineaddict
1
@machineaddict это поддерживает ассоциативные ключи.
Матей Свайгер
318

PHP 7+

По состоянию на PHP 7, это можно сделать с помощью сжато usortс анонимной функцией , которая использует оператор космического корабля для сравнения элементов.

Вы можете сделать сортировку по возрастанию, например так:

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

Или сортировка по убыванию:

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

Чтобы понять, как это работает, обратите внимание, что usortтребуется пользовательская функция сравнения, которая должна вести себя следующим образом (из документов):

Функция сравнения должна возвращать целое число меньше, равно или больше нуля, если первый аргумент считается соответственно меньше, равен или больше второго.

И обратите внимание <=>, что оператор космического корабля,

возвращает 0, если оба операнда равны, 1, если левый больше, и -1, если правый больше

что именно то, что usortнужно. Фактически, почти полное обоснование для добавления <=>к языку в https://wiki.php.net/rfc/combined-comparison-operator заключается в том, что

делает написание заказа обратного вызова для использования с usort()проще


PHP 5.3+

В PHP 5.3 введены анонимные функции, но пока нет оператора космического корабля. Мы все еще можем использовать usortдля сортировки наш массив, но он немного более многословен и труден для понимания:

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

Обратите внимание, что хотя компараторы, работающие с целочисленными значениями, довольно часто просто возвращают разницу значений, например $item2['price'] - $item1['price'], мы не можем безопасно сделать это в этом случае. Это потому, что цены - это числа с плавающей запятой в примере задающего вопрос, но функция сравнения, которую мы передаем usort, должна возвращать целые числа для usortправильной работы:

Возвращение нецелых значений из функции сравнения, такой как float, приведет к внутреннему приведению к целому числу возвращаемого значения обратного вызова. Таким образом, такие значения, как 0,99 и 0,1, будут преобразованы в целочисленное значение 0, которое будет сравнивать такие значения как равные.

Это важная ловушка, которую следует учитывать при использовании usortв PHP 5.x! Моя первоначальная версия этого ответа сделала эту ошибку, и все же я набрал десять голосов за тысячи просмотров, очевидно, никто не заметил серьезную ошибку. Простота, с которой такие недоверчивые люди, как я, могут испортить функции компаратора, как раз и является причиной того, что в PHP 7 был добавлен простой в использовании оператор космического корабля.

Марк Эмери
источник
8
Извините, но этот подход удаляет строковые ключи из ассоциативных массивов. Вместо этого следует использовать функцию "uasort".
Matteo-SoftNet
8
@DotMat Интересно - я не знал о uasort. Однако, посмотрев документы, этот ответ все еще верен в этом случае . В примере OP сортируемый массив имеет последовательные числовые индексы, а не строковые индексы, поэтому usortон более уместен. Использование uasortпоследовательно индексируемого массива приведет к отсортированному массиву, который не упорядочен по числовым индексам, так что первый элемент, видимый в foreachцикле, отсутствует $your_array[0], что вряд ли будет желательным поведением.
Марк Амери
100

В то время как другие правильно предложили использование array_multisort(), по какой-то причине ни один из ответов не подтверждает существование array_column(), что может значительно упростить решение. Так что мое предложение будет:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);
Мариано Иглесиас
источник
1
По какой-то причине я не смог заставить его работать со строками, имеющими строчные / заглавные буквы. Даже используя SORT_FLAG_CASE. Для сравнения строк мне помогло следующее: array_multisort (array_map (strtolower, array_column ($ ipr_projects, 'Name')), SORT_ASC, $ ipr_projects);
Пабамато
8
Это самый элегантный ответ. Должны быть оценены гораздо выше!
Армин Хирстеттер
3
самый короткий и простой, принятый на мой взгляд
StudioX
1
Хорошие вещи здесь. Отлично сработало для меня!
Funk Doc
Работал как шарм, ты
Leif_Lundberg
42

Поскольку ваши элементы массива сами являются массивами со строковыми ключами, лучше всего определить пользовательскую функцию сравнения. Это довольно быстро и легко сделать. Попробуй это:

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

Производит следующее:

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

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

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

)
zombat
источник
4
В сочетании с некоторыми другими комментариями здесь (uasort и встроенными анонимными функциями), вы получите этот однострочный:uasort( $inventory, function ($a, $b) { if ( $a==$b ) return 0; else return ($a > $b) ? -1 : 1; });
Алан Портер
@AlanPorter usortкажется более подходящим, чем uasortдля сортировки массива с последовательными числовыми ключами. Завершение работы с массивом, где первый элемент находится в индексе, 1а второй элемент в индексе, 0- странное поведение и надежная ловушка для людей, которые не знакомы с деталями массивов PHP; usortдает вам результат, который вы ожидаете интуитивно.
Марк Амери
25

Я закончил на этом:

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

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

Просто вызовите функцию, передав массив и имя поля массива второго уровня. Подобно:

sort_array_of_array($inventory, 'price');
Danielzt
источник
1
Ха! Я просто сделал почти то же самое и собирался опубликовать, но видел твои ... проголосовали.
Роб Эванс
1
Пониженное голосование, потому что это то же самое решение, которое Джош Дэвис опубликовал несколько лет назад.
Марк Эмери
Не согласен ... Я не сказал, что это другое решение, я просто сказал, что я закончил с этим решением и предоставил полную рабочую функцию.
Danielzt
1
@MarkAmery Я предпочитаю ответы, содержащиеся в функциях. Это поощряет копировщиков использовать функции и, надеюсь, писать меньше кода для спагетти.
Гусь
19

Вы можете использовать usortс анонимной функцией, например,

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });
kenorb
источник
Версии PHP 5> = 5.5.0, PHP 7 для тех из вас, как я, которые действительно хотели, чтобы это работало на них ..
Мэтт Р
1
Примечательно, что strnatcmp, предназначенный для сравнения строк, кажется, здесь работает нормально. По-видимому, «естественный порядок», который он реализует, включает в себя сортировку числовых строк численно, а не лексически.
Марк Амери
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");
}

выходы:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9
danamlund
источник
6

От сортировки массива ассоциативных массивов по значению данного ключа в PHP :

uasort ( http://php.net/uasort ) позволяет вам сортировать массив по вашей собственной определенной функции. В вашем случае это просто:

$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
Этот ответ появился в очереди проверки низкого качества, вероятно, потому что вы не предоставили никакого объяснения кода. Если этот код отвечает на вопрос, рассмотрите возможность добавления текста, объясняющего код в вашем ответе. Таким образом, у вас гораздо больше шансов получить больше голосов и помочь спрашивающему узнать что-то новое.
LMO
1
Пфф. это лучший ответ.
commonpike
1
-1; cmpфункция здесь не так. Он должен возвращать «целое число меньше, равно или больше нуля, если первый аргумент считается соответственно меньше, равен или больше второго», но вместо этого возвращает trueили false. Тем не менее, это замечательно, что все же работает - возможно, потому что текущая реализация usortи friends обрабатывает случаи «меньше» и «равно» одинаково - но не рассчитывает на то, что это продолжит работу в будущих версиях PHP. Если они попытаются сделать сортировки стабильными (то есть не перемещаться вокруг равных элементов без необходимости), это сломается.
Марк Амери
Кроме того, usortбыло бы более уместно, чем uasortздесь, так как uasortсохраняет связь между ключами и значениями, которая является запутанной и неожиданной при работе с последовательным числовым массивом. Например, индексы $arrayвыше после вызова uasort2, 0 и 1, в этом порядке. Если вы по какой-то причине этого не хотите, вам, вероятно, будет удобнее использовать usortэтот метод, который переиндексирует массив и переупорядочивает его.
Марк Амери
5

Было протестировано на 100 000 записей: время в секундах (рассчитано по фунции времени). Только для уникальных значений при сортировке ключевых позиций.

Решение функции @Josh Davis: Время работы : 1.5768740177155

Шахтный раствор: Время отработки: 0,094044923782349

Решение:

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
источник
7
Требование уникальных ключей сортировки, тем не менее, является своего рода нарушителем условий сделки. Если у вас есть уникальные значения сортировки, которые могут быть ключами, возникает вопрос: почему бы просто не создать массив с этими ключами для начала? В сценарии ОП трудно представить, что два предмета с одинаковой ценой были бы невозможны . Следует помнить, что использование этого решения приведет к таинственному и бесшумному исчезновению элементов из массива из отсортированного набора результатов.
Крис Бейкер
@ Крис Бейкер, ты прав. Это работает только для уникальных значений. Но это решение работает очень быстро, поэтому скорость была причиной его создания и использования. На данный момент это может быть не актуально, нужно протестировать его с PHP 7.1.x.
Nefelim
4

Я использую uasortкак это

<?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
источник
3

Эта функция может использоваться повторно:

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

По умолчанию он хорошо работает со строковыми значениями, но вам придется добавить обратный вызов для функции сравнения чисел, если все ваши значения являются числами.

mpen
источник
Вы называете это, usortarrно затем звонитеuasort вместо usort; возможно, немного сбивает с толку. Последнее - в случае последовательного массива с числовыми индексами, подобного тому, который показан в вопросе - вероятно, то, что вы действительно хотите.
Марк Амери
2

Вы можете попытаться определить свою собственную функцию сравнения, а затем использовать usort .

Алекс Секстон
источник
Да. Я сделаю это, если не смогу найти решение. Я почти уверен, что есть некоторые странные параметры, которые вы можете добавить к одному из видов для достижения этой цели. Спасибо за ваши мысли, хотя!
Мэтт
2

Вот метод, который я нашел давно и немного почищен. Это прекрасно работает и может быть быстро изменено, чтобы принимать объекты.

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

}

Чтобы изменить метод сортировки объектов, просто измените следующую строку:

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

Для запуска метода просто сделайте

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

lzoesch
источник
Этот подход работает, но немного менее лаконичен, чем ответ Джоша Дэвиса (с array_multisort) или мой (с usort), и, кажется, не дает никаких преимуществ перед ними в обмен.
Марк Амери
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

попробуй это:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);
Джамшид
источник
Привет и добро пожаловать в stackoverflow, и спасибо за ответ. Хотя этот код может ответить на вопрос, можете ли вы добавить какое-нибудь объяснение того, какую проблему вы решили, и как вы ее решили? Это поможет будущим читателям лучше понять ваш ответ и извлечь из него уроки.
Плутян
0

Завершить динамическую функцию Я перешел сюда для сортировки ассоциативных массивов и нашел эту удивительную функцию на http://php.net/manual/en/function.sort.php . Эта функция очень динамичная, которая сортирует по возрастанию и убыванию с указанным ключом.

Простая функция для сортировки массива по определенному ключу. Поддерживает связь индекса

<?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
Ахмад Сайид
источник
0

Для PHP 7 и более поздних версий.

/**
 * A method for sorting associative arrays by a key and a direction.
 * Direction can be ASC or DESC.
 *
 * @param $array
 * @param $key
 * @param $direction
 * @return mixed $array
 */
function sortAssociativeArrayByKey($array, $key, $direction){

    switch ($direction){
        case "ASC":
            usort($array, function ($first, $second) use ($key) {
                return $first[$key] <=> $second[$key];
            });
            break;
        case "DESC":
            usort($array, function ($first, $second) use ($key) {
                return $second[$key] <=> $first[$key];
            });
            break;
        default:
            break;
    }

    return $array;
}

Применение:

$inventory = sortAssociativeArrayByKey($inventory, "price", "ASC");
Арда Басоглу
источник
-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;


} 
Чираг Пипария
источник
Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код. Также постарайтесь не переполнять ваш код пояснительными комментариями, так как это снижает удобочитаемость кода и пояснений!
До свидания, StackExchange
-5

попробуй это:

asort($array_to_sort, SORT_NUMERIC);

для справки смотрите это: http://php.net/manual/en/function.asort.php

см. различные флаги сортировки здесь: http://www.php.net/manual/en/function.sort.php

sarsnake
источник
это не будет работать для многомерных массивов, но только помогло мне с другой проблемой, спасибо :)
schellmax
4
Это не может быть использовано для сортировки списка словарей по определенному ключу словаря, и, следовательно, не отвечает на поставленный вопрос.
Марк Амери