Verification: a143cc29221c9be0

Php array упорядочить по ключу

Классическое решение

Естественно первое про что вы подумали, это сортировка многомерного массива с помощью uasort, да? Набросаем вот такой вот код:

function cmp($a, $b) {

    if ($a['year'] == $b['year']) {

        return 0;

    }

    return ($a['year'] $b['year']) ? -1 : 1;

}

for($i=0; $i100000; $i++){

    $data_tmp=$data;

    uasort($data_tmp, 'cmp');

}

Запускаем, и засекаем время выполнения… Итого: 13.15 сек. Долговато и не впечатляет.

Ищем пути решения проблемы, находим на php.net, другой вариант функции сравнения, который, как там написано, должен работать быстрее:

function cmp($a, $b) {

    return strcmp($a['year'], $b['year']);

}

for($i=0; $i100000; $i++){

    $data_tmp=$data;

    uasort($data_tmp, 'cmp');

}

Итого: 23.11 сек. Хреновая оптимизация…

Ладно, хорошо, со временем выполнения мы определились. Давайте попробуем определится с «расширяемостью кода». Допустим нам поставили задачу отсортировать сначала по ключу year а затем по ключу author. Для этого нам приходится переписывать всю «дополнительную функцию», в итоге получаем что то похожее на это:

function cmp($a, $b) {

    if ($a['year'] == $b['year']) {

        if ($a['author'] == $b['author']){

            return 0;

        }else{

            return ($a['author'] $b['author']) ? -1 : 1;

        }

    }else{

        return ($a['year'] $b['year']) ? -1 : 1;

    }

}

Громоздко. Сложно изменять. Вообщем отстой, на мой взгляд.

Итак, подведем итоги. Минусы:

  • Долго выполняется
  • Сложно расширять
  • Под каждую сортировку нужна своя, новая функция

Плюсы:

  • Единственный очевидный вариант (?)

Пробуем костыли

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//Функция сортировки по ключу

function amsort($array, $key) {

    $result = array();

    $values = array();

    foreach ($array as $id => $value) {

        $values[$id] = $value[$key];

    }

    asort($values);

    foreach ($values as $key => $value) {

        $result[$key] = $array[$key];

    }

    return $result;

}

for($i=0; $i100000; $i++){

    $data_tmp=$data;

    amsort($data_tmp);

}

Засекаем. Получаем: 7.90 сек. Ну уже не плохо впринципе.

Да вот только заставить этот костыль сортировать по двум ключам уже не получится, к сожалению. Подведем итоги. Минусы:

  • Невозможно расширять
  • Сортировка только по одному ключу

Плюсы

  • Приемлимая скорость выполнения
  • Одна функция для разных видов сортировки

Функция array_multisort

Оказывается разработчики PHP уже давным давно все придумали до нас. Оказывается есть функция array_multisort. Как работает эта функция:
array_multisort( array &$arr [, array &$arr [, array &$arr... ]] )
Грубо говоря каждый массив будет отсортирован в соответствии с предыдущим массивом. Вообщем пример:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//Сортируемый масив

$array_main=array(

'foo',

'bar',

'foobar',

);

//Определяющий массив

$array_id=array(

3,

1,

2,

);

array_multisort($array_id, $array_main);

var_dump($array_main);

Выведет:

array(4) {

[0]=> string(3) "bar"

[1]=> string(3) "foobar"

[2]=> string(3) "foo"

}

А это как раз то что нам надо! Возвращаемся к тестам на скорость:

$data_year=array();

//Генерируем "определяющий" массив

foreach($data as $key=>$arr){

    $data_year[$key]=$arr['year'];

}

for($i=0; $i100000; $i++){

    $data_tmp=$data;

    array_multisort($data_year, SORT_NUMERIC, $data_tmp);

}

Засекаем. Получаем: 3.87 сек. Это рекорд!

Ну то что это самый быстрый вариант мы определили. Это хорошо. А как насчет расширяемости? Как к примеру заставить сортировать массив по двум ключам? Оказывается с этой функцией очень просто! Достаточно добавить еще один «определяющий массив», вот так:

$data_author=array();

foreach($data as $key=>$arr){

    $data_author[$key]=$arr['author'];

}

$data_year=array();

foreach($data as $key=>$arr){

    $data_year[$key]=$arr['year'];

}

array_multisort($data_year, SORT_NUMERIC, $data_author, $data);

var_export($data);

На выходе получим вот такой массив:

array(

  0 => array('text' => 'str3', 'year' => '2009', 'author' => 20, ),

  1 => array('text' => 'str8', 'year' => '2009', 'author' => 20, ),

  2 => array('text' => 'str1', 'year' => '2010', 'author' => 10, ),

  3 => array('text' => 'str5', 'year' => '2010', 'author' => 20, ),

  4 => array('text' => 'str4', 'year' => '2010', 'author' => 30, ),

  5 => array('text' => 'str2', 'year' => '2011', 'author' => 10, ),

  6 => array('text' => 'str6', 'year' => '2011', 'author' => 10, ),

  7 => array('text' => 'str7', 'year' => '2011', 'author' => 20, ),

);

Как видите функция справилась со своей задачей. Наш массив отсортирован сначала по year, затем по author. А с помощью различных флагов типа SORT_DESC, SORT_ASC и тд можно добится любой сортировки (про них подробнее смотрите в мане), так что это на мой взгляд самый лучший вариант для использования в своих скриптах.

Минусы

  • ??

Плюсы

  • Лучшая скорость
  • Расширяемость
  • Функциональность