2013-03-21 3 views
0

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

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

Давайте в него:

В этом примере мы будем сортировать список форматов печати. Мы будем использовать только два возможных свойства.

Пользователь настраивает процесс сортировки в файле INI:

sort_priority="special_deal:desc,ratio:asc" 

Описание:

// special_deal -> This is a binary flag - if set to 1 the print format is a special deal and should therefore be presented first 
// ratio -> This is the ratio of the given print format (i.e. 0.75 (that's a 3:4 format) or 1 (that's a 1:1 format)) 

В коде конфигурации разбивается на части:

$toSort=array(<OUR ARRAY WE WANT TO SORT>); 

$sortKeys=explode(',', 'special_deal:desc,ratio:asc'); 

// we then iterate through the defined keys 
foreach($sortKeys as $sortKey){ 

    // we put together the name of the predefined sort function 
    if(strstr($sortKey, ':')) { 
     list($skey,$sdir)=explode(':', $sortKey); 
     $methodName='sort_by_'.$skey.'_'.$sdir; 
    } else $methodName='sort_by_'.$sortKey.'_asc'; 

    // so $methodName can (for instance) be: sort_by_special_deal_asc 
    // or: sort_by_ratio_desc 

    // if the sort function is available, we apply it 
    if(is_callable($methodName)) 
     usort($toSort, $methodName); 
} 

И наши функции сортировки выглядят так:

function sort_by_special_deal_asc($a, $b){ 
    return ($a['specialDeal']!=$b['specialDeal']); 
} 
function sort_by_special_deal_desc($a, $b){ 
    return ($a['specialDeal']==$b['specialDeal']); 
} 
function sort_by_ratio_asc($a, $b){ 
    if($a==$b) return 0; 
    return $a['ratio']<$b['ratio'] ? -1 : 1; 
} 
function sort_by_ratio_desc($a, $b){ 
    if($a==$b) return 0; 
    return $a['ratio']>$b['ratio'] ? -1 : 1; 
} 

О проблеме под рукой ...

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

1.) Sort all entries so that the ones that are a special deal come first 
2.) Then sort all entries by their ratio 

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

$formats=array(
    array(
     'format' => '30x40', 
     'ratio' => 0.75 
    ), 
    array(
     'format' => '60x90', 
     'ratio' => 0.667 
    ), 
    array(
     'format' => '50x50', 
     'ratio' => 1 
    ), 
    array(
     'format' => '60x80', 
     'ratio' => 0.75, 
     'specialDeal' => 1 
    ) 
); 

И желаемый результат, учитывая выше функция сортировки, должен быть:

$formats=array(
    array(
     'format' => '60x80', 
     'ratio' => 0.75, 
     'specialDeal' => 1 
    ), 
    array(
     'format' => '60x90', 
     'ratio' => 0.667 
    ), 
    array(
     'format' => '30x40', 
     'ratio' => 0.75 
    ), 
    array(
     'format' => '50x50', 
     'ratio' => 1 
    ), 
); 

Я надеюсь, что это объясняет проблему должным образом.

Может ли кто-нибудь указать мне в правильном направлении здесь? Как я могу достичь этого, динамически, в лучшем случае, используя usort()?

Спасибо!

EDIT: Обратите внимание: мои функции сравнения (см. Выше) были неисправны. Существовали две проблемы:

1.) Возвращаемое логическое значение было неправильным - возврат -1, 0 или 1 был способом. 2.) Сравнение $ a и $ b как полных массивов/объектов было неправильным - право сравнить конкретные значения в пределах этих массивов (те, которые функция должна сравнивать).

Подробнее см. В принятом ответе и разделе комментариев.

ответ

1

Построить массив, как это разбор пользователя сортировать предпочтение:

$sortMethods = array('sort_by_ratio_desc', 'sort_by_special_deal_asc'); 

Затем сортировать используя сравнение, как это:

usort($array, function ($a, $b) use ($sortMethods) { 
    foreach ($sortMethods as $method) { 
     $result = $method($a, $b); 
     if ($result != 0) { 
      break; 
     } 
    } 
    return $result; 
}); 
+0

Вау, я даже не знал о «использовании» ключевого слова. Спасибо. Я немедленно применим это и посмотрю, будет ли это работать для меня. – SquareCat

+0

Работает в PHP 5.3+ с анонимными функциями. – deceze

+0

Я применил ваше решение, но пока оно отлично работает для одного свойства сортировки, любые последующие свойства сортировки, похоже, игнорируются. Я проверил много раз, но код соответствует вашему описанию. Возможно, я упустил что-то жизненно важное? – SquareCat

0

ЗАКАНЧИВАТЬ комментарии для uasort в руководстве php.net - http://php.net/manual/en/function.uasort.php

В частности, с динамическими обратными вызовами, отправленными dholmes.

+0

Спасибо. Это может быть из-за отсутствия опыта на моей стороне, но я не вижу, как применить это. – SquareCat

 Смежные вопросы

  • Нет связанных вопросов^_^