如何重新排序这个数组?

时间:2011-09-27 07:32:13

标签: php sorting multidimensional-array

我有一个数据库表如下:

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

这会返回图片中的所有列标题,但最重要的是 slug (不确定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,而{1}}中的slug需要加载parent之前它在父母中定义。

如果它直接在它之前并不重要。例如,您看到testing是第一个返回的slug,但是这个的父级是最后一个slug(google.com)。因此,只要定义了父级的slug行被排序,以便它在父列中具有slug值的行之前,一切都很好。

所以在这种情况下,它可以重新排序为以下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',
    )
);

所有这三个有序数组都可以工作,因为slugparent匹配的数组位于匹配parent的数组之前,并且因为slug值{{{ 1}}与这个数组顺序不重要的任何sub_test_1值都不匹配,因此该数组可以位于数组中的任何位置。

我该怎么做?我正在考虑以某种方式循环遍历数组,并试图确定slug是否在任何父母中,并且只是以某种方式进行重新排序......

简而言之,如果parent与数组中的slug匹配,则parent只需在parent之前订购。否则,如果未找到匹配项,则订单并不重要。

4 个答案:

答案 0 :(得分:3)

正如Niko所建议的,数据库支持强大的排序功能,因此通常可以通过告诉数据库以哪种顺序来返回数据来最好地解决这个问题。如果使用SQL查询数据,那就是ORDER BY子句。这在数据库的文档中指定,假设您使用的是MySQL 5.0:http://dev.mysql.com/doc/refman/5.0/en/sorting-rows.html

如果无法影响数据库级别的顺序,则需要在PHP中对数组进行排序。实际上你有一个数组数组,其中外部数组只是一个列表,每行有id(主键),其他字段作为字段名 - > value数组作为值(内部数组)。

您的排序是*用户定义的 - 您指定排序顺序。一种常见的方法是使用sort函数来比较彼此之间的两个条目。该排序函数需要确定这两个中的哪一个具有比另一个更高的排序顺序(或两者具有相同的权重)。在你的情况下,如果一个项目是另一个项目的孩子,则一个项目高于另一个项目。

这是一般原则。您可以定义决定的排序函数(所谓的回调函数),PHP会使用数组数据来提供它,以便使用usortDocs函数进行排序。

您需要解决的一个子问题是决定整个数组中是否存在子元素(具有与父元素具有相同值的slug的项目)。因为这看起来可能有点复杂,所以将这一切封装到它自己的类中是明智的。

示例/ Demo

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,如outlined below。主代码不需要更改,因为它只使用sortChildsFirst方法。

答案 1 :(得分:1)

您可以使用usort()函数对数组进行排序。

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

答案 2 :(得分:1)

由于你的结构是树状的,所以首先想到的是用它构建一棵树。它是这样的:

$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];
}

这将创建一组对象,其中成员datasub =子对象列表。 现在我们迭代树,并为每个“根”节点,将它及其子节点添加到已排序的数组中:

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

希望这会有所帮助。

答案 3 :(得分:-1)

好的,首先让我感谢大家的详细解释。它们非常直观。但是,我找到了另一种方法,如果你发现这种方法有什么问题,请问我们能告诉我吗?

Click here to see a Demo of this working!

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

似乎在我测试过的所有情况下工作,但当然,它们可能是某个地方的缺陷。你们都怎么看待这个?