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

268

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

$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),

);

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

Теги:
sorting

16 ответов

377
Лучший ответ

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

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

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);
  • 5
    Хотя это определенно дороже, чем альтернативы.
  • 3
    Более дорогой? Это странно, на моей машине (с PHP 5.3.1-dev) array_multisort () на несколько процентов быстрее для маленьких массивов и до 100 раз быстрее для больших массивов (более 100 элементов)
Показать ещё 9 комментариев
150

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.

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

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

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
        )

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

Я закончил:

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');
  • 1
    Ха! Я просто сделал почти то же самое и собирался опубликовать, но видел твои ... проголосовали.
  • 1
    Пониженное голосование, потому что это то же самое решение, которое Джош Дэвис опубликовал несколько лет назад.
Показать ещё 2 комментария
9

В то время как другие правильно предложили использовать 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);
  • 6
    Это самый элегантный ответ. Должны быть оценены гораздо выше!
Показать ещё 1 комментарий
9

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

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });
  • 0
    Версии PHP 5> = 5.5.0, PHP 7 для тех из вас, как я, которые действительно хотели, чтобы это работало на них ..
7
$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
3

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

Решение функции @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*
}
  • 7
    Однако требование уникальных ключей сортировки является своего рода нарушителем условий сделки. Если у вас есть уникальные значения сортировки, которые могут быть ключами, возникает вопрос: почему бы просто не создать массив с этими ключами для начала? В сценарии ОП трудно представить, что два предмета с одинаковой ценой были бы невозможны . Следует помнить, что использование этого решения приведет к таинственному и бесшумному исчезновению элементов из массива из отсортированного набора результатов.
  • 0
    Благодарю. Это работает для меня
Показать ещё 1 комментарий
1

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

  • 0
    Да. Я сделаю это, если не смогу найти решение. Я почти уверен, что есть некоторые странные параметры, которые вы можете добавить к одному из видов для достижения этой цели. Спасибо за ваши мысли, хотя!
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

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

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

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

0
//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
    )

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


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

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

$array = array('0'=>array('price'=>'1000.50','product'=>'test1'),
          '1'=>array('price'=>'8800.50','product'=>'test2'),
          '2'=>array('price'=>'200.0','product'=>'test3'));
    function cmp($a, $b)
    {
    $p1 = $a['price'];
    $p2 = $b['price'];
    return (float)$p1 > (float)$p2;
    }
    @uasort($array, "cmp");
  • 1
    Этот ответ появился в очереди проверки низкого качества, вероятно, потому что вы не предоставили никакого объяснения кода. Если этот код отвечает на вопрос, рассмотрите возможность добавления текста, объясняющего код в вашем ответе. Таким образом, у вас гораздо больше шансов получить больше голосов и помочь спрашивающему узнать что-то новое.
  • 1
    Пфф. это лучший ответ.
-1
  <?php

$inventory = array(

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

);



function myfunc($a,$b){
return strnatcmp($a['price'],$b['price']);
}
$result=usort ($inventory,"myfunc");?>
<pre><?php print_r(array_reverse($inventory)); ?></pre>

простое решение:)

вывод:

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

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

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

)
  • 0
    Этот ответ появился в очереди проверки низкого качества, вероятно, потому что вы не предоставили никакого объяснения кода. Если этот код отвечает на вопрос, рассмотрите возможность добавления текста, объясняющего код в вашем ответе. Таким образом, у вас гораздо больше шансов получить больше голосов и помочь спрашивающему узнать что-то новое.
-2

попробуйте следующее:

asort($array_to_sort, SORT_NUMERIC);

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню