如何对关联数组进行递归排序

时间:2019-05-22 15:29:43

标签: php arrays sorting recursion multidimensional-array

我想对以下关联数组进行排序:

$tree = [
  "id" => 245974,
  "children" => [
    [
      "id" => 111
    ],
    [
      "id" => 245982,
      "children" => [
        [
          "id" => 246093,
          "children" => [
            [
              "id" => 225892
            ],
            [
              "id" => 225893
            ],
            [
              "id" => 225902
            ]
          ]
        ]
      ]
    ]
  ]
];

id => 225902的“搜索值”之后所需的排序顺序:

[
  "id" => 245974,
  "children" => [
    [
      "id" => 245982, // <-- this is moved up
      "children" => [
        [
          "id" => 246093,
          "children" => [
            [
              "id" => 225902 // <-- this is moved up
            ],
            [
              "id" => 225892
            ],
            [
              "id" => 225893
            ]
          ]
        ]
      ]
    ],
    [
      "id" => 111
    ]
  ]
];

我尝试过的事情:

<?php

$category_id = 225902;

function custom_sort(&$a, &$b) {
  global $category_id;

  if ($a['id'] === $category_id) {
    return -1;
  }

  if ($b['id'] === $category_id) {
    return 1;
  }

  if (array_key_exists('children', $a)) {
    if (usort($a['children'], "custom_sort")) {
      return -1;
    }
  }

  if (array_key_exists('children', $b)) {
    if (usort($b['children'], "custom_sort")) {
      return 1;
    }
  }

  return 0;
}

function reorder_tree($tree) {
  usort($tree['children'], "custom_sort");
  return $tree;
}

echo "<pre>";
var_dump(reorder_tree($tree));
echo "</pre>";

但是,返回值:

[
  "id" => 245974,
  "children" => [
    [
      "id" => 245982, // <- this is moved up
      "children" => [
        [
          "id" => 246093,
          "children" => [
            [
              "id" => 225892
            ],
            [
              "id" => 225893
            ],
            [
              "id" => 225902 // <- this is *not* moved up
            ]
          ]
        ]
      ]
    ],
    [
      "id" => 111
    ],
  ]
];

如何对children数组进行排序?

1 个答案:

答案 0 :(得分:3)

伟大的尝试,并且非常正确。比较器中的递归问题是:usort在数组长度为1时不会调用比较器函数,因此,是否探索整个树都是usort的想法。这将放弃id => 245982的树分支。

解决方案是避免直接在usort的比较器函数中重复。而是使用常规的递归函数根据需要调用usort,即当前数组或子数组包含目标ID。我使用一个单独的数组来跟踪应该向前移动哪些元素,但是如果愿意,您可以打破循环,将单个元素拼接/取消移位到前面。

我们还可以使$category_id成为函数的参数。

这是一种方法:

function reorder_tree_r(&$children, $target) {
    $order = [];
    $should_sort = false;

    foreach ($children as $i => &$child) {
        $order[$i] = false;

        if (array_key_exists("children", $child) &&
            reorder_tree_r($child["children"], $target) || 
            $child["id"] === $target) {
            $order[$i] = true;
            $should_sort = true;
        }
    }

    if ($should_sort) {
        $priority = [];
        $non_priority = [];

        for ($i = 0; $i < count($children); $i++) {
            if ($order[$i]) {
                $priority[]= $children[$i];
            }
            else {
                $non_priority[]= $children[$i];
            }
        }

        $children = array_merge($priority, $non_priority);
    }

    return $should_sort;
}

function reorder_tree($tree, $target) {
    if (!$tree || !array_key_exists("children", $tree)) {
        return $tree;
    }

    reorder_tree_r($tree["children"], $target);
    return $tree;
}


var_export(reorder_tree($tree, 225902));

输出:

array (
  'id' => 245974,
  'children' => 
  array (
    0 => 
    array (
      'id' => 245982,
      'children' => 
      array (
        0 => 
        array (
          'id' => 246093,
          'children' => 
          array (
            0 => 
            array (
              'id' => 225902,
            ),
            1 => 
            array (
              'id' => 225892,
            ),
            2 => 
            array (
              'id' => 225893,
            ),
          ),
        ),
      ),
    ),
    1 => 
    array (
      'id' => 111,
    ),
  ),