Как переупорядочить этот массив? - PullRequest
5 голосов
/ 27 сентября 2011

У меня есть таблица базы данных следующим образом:

couldn't be bothered with an image description, I guess

Возвращает все заголовки столбцов на рисунке, но наиболее важными являются slug и parent (не уверен насчет id_button).

Массив автоматически упорядочивается id_button ASC, что меня действительно раздражает.Но, в любом случае, это не важно, поскольку мне нужно упорядочить его по-другому или изменить порядок после заполнения массива.

Массив возвращает это в порядке id_button:

$new_menu_buttons = array(
    0 => array(
            'id_button' => 1,
            'parent' => 'help',
            'position' => 'child_of',
            'slug' => 'testing',
    ),
    1 => array(
            'id_button' => 2,
            'parent' => 'packages',
            'position' => 'after',
            'slug' => 'sub_test_1',
    ),
    2 => array(
            'id_button' => 3,
            'parent' => 'google.com',
            'position' => 'after',
            'slug' => 'another_test',
    ),
    3 => array(
            'id_button' => 4,
            'parent' => 'testing'
            'position' => 'child_of',
            'slug' => 'google.com',
    )
);

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

Это не важно, если это прямо перед ним.Например, вы видите, что testing является первым slug, который возвращается, и все же родительским для него является последний слаг (google.com).Таким образом, до тех пор, пока строка пули, в которой определен родительский элемент, упорядочена таким образом, что ДО строки, имеющей значение пули в родительском столбце, все в порядке.

Так что в этой ситуации ее можно переупорядочитькак любой из этих 3 упорядоченных массивов ниже:

$new_menu_buttons = array(
    0 => array(
            'id_button' => 1,
            'parent' => 'help',
            'position' => 'child_of',
            'slug' => 'testing',
    ),
    1 => array(
            'id_button' => 2,
            'parent' => 'packages',
            'position' => 'after',
            'slug' => 'sub_test_1',
    ),
    2 => array(
            'id_button' => 4,
            'parent' => 'testing',
            'position' => 'child_of',
            'slug' => 'google.com',
    ),
    3 => array(
            'id_button' => 3,
            'parent' => 'google.com'
            'position' => 'after',
            'slug' => 'another_test',
    )
);

ИЛИ это ...

$new_menu_buttons = array(
    0 => array(
            'id_button' => 1,
            'parent' => 'help',
            'position' => 'child_of',
            'slug' => 'testing',
    ),
    1 => array(
            'id_button' => 4,
            'parent' => 'testing',
            'position' => 'child_of',
            'slug' => 'google.com',
    ),
    2 => array(
            'id_button' => 2,
            'parent' => 'packages',
            'position' => 'after',
            'slug' => 'sub_test_1',
    ),
    3 => array(
            'id_button' => 3,
            'parent' => 'google.com'
            'position' => 'after',
            'slug' => 'another_test',
    )
);

ИЛИ даже это ...

$new_menu_buttons = array(
    0 => array(
            'id_button' => 1,
            'parent' => 'help',
            'position' => 'child_of',
            'slug' => 'testing',
    ),
    1 => array(
            'id_button' => 4,
            'parent' => 'testing',
            'position' => 'child_of',
            'slug' => 'google.com',
    ),
    2 => array(
            'id_button' => 3,
            'parent' => 'google.com'
            'position' => 'after',
            'slug' => 'another_test',
    ),
    3 => array(
            'id_button' => 2,
            'parent' => 'packages',
            'position' => 'after',
            'slug' => 'sub_test_1',
    )
);

Все 3 изэти упорядоченные массивы будут работать, потому что массив с slug, который соответствует parent, находится перед массивом с совпадающим parent, и, поскольку значение slug, sub_test_1 не соответствует ни одному из значений parentэтот порядок массивов не имеет значения, поэтому массив может быть расположен в любом месте массива.

Как я могу это сделать?Я думаю о том, чтобы просто каким-то образом пройтись по массиву и попытаться определить, есть ли слаг в каком-либо из родителей, и просто сделать переупорядочение каким-либо образом ...

Короче, slug должен бытьзаказывается до parent ТОЛЬКО если в массиве есть parent, который соответствует slug.В противном случае, если совпадений не найдено, порядок не важен.

Ответы [ 4 ]

3 голосов
/ 27 сентября 2011

Как предложил Нико, базы данных поддерживают мощные функции сортировки, поэтому обычно лучше всего решить эту проблему, указав базе данных , в каком порядке , вернуть данные.Если данные запрашиваются с SQL, это предложение ORDER BY.Это указано в документации вашей базы данных, при условии, что вы используете MySQL 5.0: http://dev.mysql.com/doc/refman/5.0/en/sorting-rows.html

Если вы не можете повлиять на порядок на уровне базы данных, вам нужно отсортировать массив вPHP.На самом деле у вас есть массив массивов, в котором внешний массив - это просто список, в котором id (первичный ключ) каждой строки и другие поля в качестве поля имени -> значение массива в качестве значения (внутреннего массива).

Ваша сортировка * определяется пользователем - вы указываете порядок сортировки.Распространенным способом является наличие функции сортировки, которая сравнивает две записи друг с другом.Эта функция сортировки должна решить, какая из этих двух имеет более высокий порядок сортировки, чем другая (или обе имеют одинаковый вес).В вашем случае один предмет выше другого, если один является дочерним по отношению к другому.

Это общий принцип.Вы определяете решающую функцию сортировки (так называемая функция callback ), и PHP заботится о том, чтобы снабдить ее данными массива для сортировки с помощью usort Docs function.

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

Пример / Демо :

class menuButtons
{
    /**
     * @var array
     */
    private $buttons;

    public function __construct(array $buttons)
    {
        $this->buttons = $buttons;
    }


    public function sortChildsFirst()
    {
        $buttons = $this->buttons;
        usort($buttons, array($this, 'sortCallback'));
        return $buttons;
    }

    private function sortCallback($a, $b)
    {
        // an element is more than any other if it's parent
        // value is any other slugs value

        if ($this->slugExists($a['parent']))
            return 1;

        return -1;
    }

    private function slugExists($slug)
    {
        foreach($this->buttons as $button)
        {
            if ($button['slug'] === $slug)
                return true;
        }
        return false;
    }
}

$buttons = new menuButtons($new_menu_buttons);

$order = $buttons->sortChildsFirst();

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

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

1 голос
/ 27 сентября 2011

Поскольку ваша структура похожа на дерево, первое, что приходит на ум, - это построить из нее дерево.Это выглядит так:

$tree = array();

foreach($array as $e) {
    $p = $e['parent'];
    $s = $e['slug'];
    if(!isset($tree[$p]))
        $tree[$p] = new stdclass;
    if(!isset($tree[$s]))
        $tree[$s] = new stdclass;
    $tree[$s]->data = $e;
    $tree[$p]->sub[] = $tree[$s];
}

Это создает набор объектов с членами data и sub = списком дочерних объектов.Теперь мы перебираем дерево и для каждого «корневого» узла добавляем его и его дочерние элементы в отсортированный массив:

$out = array();

foreach($tree as $node)
    if(!isset($tree[$node->data['parent']]))
        add($out, $node);

, где add() равно

function add(&$out, $node) {
    if(isset($node->data))
        $out[] = $node->data;
    if(isset($node->sub))
        foreach($node->sub as $n)
            add($out, $n);
}

, надеюсь, это поможет.

1 голос
/ 27 сентября 2011

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

http://php.net/manual/en/function.usort.php

0 голосов
/ 27 сентября 2011

Хорошо, сначала позвольте мне поблагодарить всех вас за ваши подробные объяснения. Они очень интуитивно понятны. Тем не менее, я нашел другой способ, вы, ребята, дайте мне знать, если вы обнаружите что-то не так с этим методом, пожалуйста?

Нажмите здесь, чтобы увидеть демонстрацию этой работы!

$temp_buttons = array();
foreach($new_menu_buttons as $buttons)
    $temp_buttons[$buttons['parent']] = $buttons['slug'];


dp_sortArray($new_menu_buttons, $temp_buttons, 'slug');

// The $new_menu_buttons array is now sorted correctly!  Let's check it...
var_dump($new_menu_buttons);

function dp_sortArray(&$new_menu_buttons, $sortArray, $sort)
{
    $new_array = array();
    $temp = array();
    foreach ($new_menu_buttons as $key => $menuitem)
    {
        if (isset($sortArray[$menuitem[$sort]]))
        {
            $new_array[] = $menuitem;

            $temp[$menuitem['parent']] = $menuitem['slug'];
            unset($new_menu_buttons[$key]);
        }   
    }

    $ordered = array();

    if (!empty($new_array))
    {
        foreach ($new_array as $key => $menuitem)
        {
            if (isset($temp[$menuitem[$sort]]))
            {
                $ordered[] = $menuitem;
                unset($new_array[$key]);
            }
        }
    }
    else
    {
        $new_menu_buttons = $new_menu_buttons;
        return;
    }

    $new_menu_buttons = array_merge($ordered, $new_array, $new_menu_buttons);
}

Кажется, работает во всех случаях, которые я тестировал, но, конечно, их где-то могло быть недостатком. Что вы все думаете об этом?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...