Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> иерархическая сортировка массива 
:(
    Опции темы
dembel
Дата 31.12.2009, 00:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 32
Регистрация: 2.2.2008

Репутация: нет
Всего: нет



Здравствуйте. Подскажите пожалуйста алгоритм или php код для сортировки иерархического массива. 

Есть массив в котором хранятся id и parent_id, нужно его отсортировать так, чтобы он в порядке иерархии и имел новый ключ level, где хранился бы уровень вложенности. Желательно без рекурсии и дополнительных библиотек.


Структура отсортированного массива была бы приблизительно такая:
Цитата

[item1] => {
                      [id] => '1';
                      [parent_id] => '0';
                      [level] => '0'
}
[item2] => {
                      [id] => '3';
                      [parent_id] => '1';
                      [level] => '1'
}
[item3] => {
                      [id] => '2';
                      [parent_id] => '3';
                      [level] => '2'
}
[item4] => {
                      [id] => '4';
                      [parent_id] => '0';
                      [level] => '0';
}


PM MAIL   Вверх
skyboy
Дата 31.12.2009, 09:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 1
Всего: 260



Цитата(dembel @  30.12.2009,  23:53 Найти цитируемый пост)
Структура отсортированного массива была бы приблизительно такая:

исходный массив надо придумать?
сначала построй "дерево"
, чтоб вложенные элементы действительно ставли вложенными и неважно, что струкутра усложнится(на поле "дети текущего элемента") и станет отличаться от целевой - потом поправим.
а потом проходимся по всему массиву при помощи array_walk_recursive. c учетом того, что array_walk_recursive возвращает только false или true, то даю идею:
а) хранить "плоский" массив и передавать его в callback-функцию не через третий параметрй($userdata), а в виде статической переменной внутри самой фунцкии
б) возвращать значение этой самой статической переменной, если первые два параметра null(признак "запуска функции вручную" - вме вызова walk_array_recursive)
Код

function flattenArray($key, item)
{
 static $result = array();
 if (is_null($key) && is_null($item))  {
   return $result;
 } else {
   $result[$item['id']] = array('id'=>$item['id'], 'parent'=> $item['parent'], 'level'=> isset($result[$item['parent']])?$result[$item['parent']]['level'] + 1: 0);
 }
}
//$array = .....
if (array_walk_recursive($array, 'flattenArray')) { 
  $result = flattenArray(null, null);
  print_r($result);
}

хотя мне кажется, что вывод при передаче null/null - это изврат

Добавлено через 6 минут и 25 секунд
впрочем, я всю усложнил. ща попроще решение набросаю.
PM MAIL   Вверх
skyboy
Дата 31.12.2009, 10:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: 1
Всего: 260



значит, более простой вариант. назовем наш массив $_source
первым делом надо сделать так, чтоб ключом элемента массива был его id.
после чего заполнить поле level:
0. заполняем поле level для всех элементов значением null(чтоб определить, где )
1. если родительского элемента нет(!isset($_source[$currentElement['parent']])), то устанавливаем у текущего элемента поле level = 0
2. если родительский элемент есть, то берем его уровень(если он не null - то есть уже рассчитан), добавляем 1 и получаем уровень текущего элемента
3. проходим по массиву, выполняя пункт 2 до тех пор, пока значения не перестанут меняться.
4. выбрасываем элементы с level = null - у этих элементов указан не существующий parent
полученный массив можно отсортировать при помощи usort. callback функция будет реализовывать следующую логику сравнения элементов $el1 и $el2:
0. если $el1['level'] == $el2['level'] && $el1['parent'] == $el2['parent'], то вернуть сравнение $el1['id'] и $el2['id']("1" если $el1['id'] больше, "0" если равны и "-1" если меньше)
1. если уровни одинаковы, но parent разные, то выполнить сравнение для parent-элементов и это же значение вернуть как сравнение текущих элементов.
2. если уровни разные, то вернуть сравнение элемента с меньшим уровнем и родителя элемента с большим уровнем.

PM MAIL   Вверх
segrey
Дата 31.12.2009, 23:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 26.12.2006

Репутация: нет
Всего: нет



Цитата(dembel @ 30.12.2009,  22:53)
Здравствуйте. Подскажите пожалуйста алгоритм или php код для сортировки иерархического массива. 

Есть массив в котором хранятся id и parent_id, нужно его отсортировать так, чтобы он в порядке иерархии и имел новый ключ level, где хранился бы уровень вложенности. Желательно без рекурсии и дополнительных библиотек.


Структура отсортированного массива была бы приблизительно такая:
Цитата

[item1] => {
                      [id] => '1';
                      [parent_id] => '0';
                      [level] => '0'
}
[item2] => {
                      [id] => '3';
                      [parent_id] => '1';
                      [level] => '1'
}
[item3] => {
                      [id] => '2';
                      [parent_id] => '3';
                      [level] => '2'
}
[item4] => {
                      [id] => '4';
                      [parent_id] => '0';
                      [level] => '0';
}


А в чём преступление если прибегнуть к рекурсии? Вобще рекурсивный алогоритм можно представить в виде обычного цикла с ручным стеком. Если не так принципиально примерно так (вобще форма хранения неудобная, приходится мапить дерево в промежуточную структуру, чтобы потом лишний раз его не перебирать):
Код

$source = array(
    'item1' => array(
        'id' => '1',
        'parent_id' => '0'
    ),
    'item2' => array(
        'id' => '3',
        'parent_id' => '1'
    ),
    'item4' => array(
    'id' => '4',
    'parent_id' => '0'
    ),
    'item3' => array(
        'id' => '2',
        'parent_id' => '3'
    )
);


function sort_subtree (&$map, &$source, $parent_level, $parent_id, &$result)
{
    if (isset($map[$parent_id])) { // у узла есть дети
        foreach ($map[$parent_id] as $map_data) {
   
            // добавляем в результат все поля + level
            $result[$map_data['key']] = $source[$map_data['key']];
            $result[$map_data['key']]['level'] = $parent_level + 1;
            
            // на уровень ниже
            sort_subtree($map, $source, $parent_level + 1, $map_data['id'], &$result);
        }
    }
}

function sort_tree (&$source)
{
    // выворачиваем структуру наизнанку, теперь ключем выступает parent_id
    $map = array();
    foreach ($source as $key => $data) {
        if (!isset($map[$data['parent_id']])) {
            $map[$data['parent_id']] = array();
        }
        
        $map[$data['parent_id']][] = array('id' => $data['id'], 'key' => $key);
    }
    
    $result = array();
    // результирующий массив гоняется по ссылке между рекурсивными вызовами и там формируется
    sort_subtree($map, $source, -1, 0, $result);
    return $result;
}

var_dump(sort_tree($source));


Это сообщение отредактировал(а) segrey - 31.12.2009, 23:38
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса

Внимание: данный раздел предназначен для решения сложных, нестандартных задач.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Для профи | Следующая тема »


 




[ Время генерации скрипта: 0.0727 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.