【问题标题】:Sort array values based on parent/child relationship根据父/子关系对数组值进行排序
【发布时间】:2016-12-31 11:53:46
【问题描述】:

我正在尝试对数组进行排序,以确保任何项的父项始终存在于数组中。例如:

Array
(
    [0] => Array
        (
            [0] => 207306
            [1] => Bob
            [2] => 
        )

    [1] => Array
        (
            [0] => 199730
            [1] => Sam
            [2] => 199714
        )

    [2] => Array
        (
            [0] => 199728
            [1] => Simon
            [2] => 207306
        )

    [3] => Array
        (
            [0] => 199714
            [1] => John
            [2] => 207306
        )

    [4] => Array
        (
            [0] => 199716
            [1] => Tom
            [2] => 199718
        )

    [5] => Array
        (
            [0] => 199718
            [1] => Phillip
            [2] => 207306
        )

    [6] => Array
        (
            [0] => 199720
            [1] => James
            [2] => 207306
        )

)

在上面的数组中这个“失败”,因为 [1][2] (Sam) 还不存在,[4][2] (Tom) 也不存在。

在这种情况下,正确的输出应该是,因为 Sam 和 Tom 的父母在他们出现在数组中之前就已经存在:

Array
(
    [0] => Array
        (
            [0] => 207306
            [1] => Bob
            [2] => 
        )

    [1] => Array
        (
            [0] => 199714
            [1] => John
            [2] => 207306
        )


    [2] => Array
        (
            [0] => 199730
            [1] => Sam
            [2] => 199714
        )

    [3] => Array
        (
            [0] => 199728
            [1] => Simon
            [2] => 207306
        )


    [4] => Array
        (
            [0] => 199718
            [1] => Phillip
            [2] => 207306
        )


    [5] => Array
        (
            [0] => 199716
            [1] => Tom
            [2] => 199718
        )

    [6] => Array
        (
            [0] => 199720
            [1] => James
            [2] => 207306
        )

)

我找到了一个答案https://stackoverflow.com/a/12961400/1278201,它非常接近,但它似乎只深了一层(即只有一个父级),而在我的例子中,层次结构中可能有 1 或 10 层。

如何对数组进行排序,以便除非其父元素之前已经存在,否则不会出现任何值?

【问题讨论】:

  • 请更清楚地解释您的问题。目前尚不清楚每个数组中索引 0 和 2 处的整数值指的是什么,因为它们太大而无法引用任何现有索引。
  • 索引 0 是每个员工的 ID,索引 2 是该员工的经理 ID,这是经理 ID 的查找,因此,在上面的示例中,John 的 ID 为 199714,并且他的经理是 207306,即 Bob,因为 Bob 的 ID 为 207306 (index0)
  • 我正在考虑投票结束这个问题,因为这更像是一个编码挑战而不是一个真正的问题。您没有发布任何解决问题的个人尝试,只是简单地发布了一些数据和一些已经给出的真正有效的解决方案的链接。通过坚持 SO 的严格标准,这不是一个好问题。考虑为您的下一个问题重新阅读stackoverflow.com/help/how-to-ask。
  • 这种特殊的关系存储方式称为adjacency list,还有well-defined ways of traversing them。

标签: php arrays sorting


【解决方案1】:

这将简单地对数组进行排序(在 O(n) 中)首先将所有没有父元素的元素,然后是其父元素已经在数组中的元素,迭代,直到没有以当前元素为父元素的子元素。

# map the children by parent
$parents = ['' => []];
foreach ($array as $val) {
    $parents[$val[2]][] = $val;
}
# start with those with no parent
$sorted = $parents[''];
# add the children the current nodes are parent of until the array is empty
foreach ($sorted as &$val) {
    if (isset($parents[$val[0]])) {
        foreach ($parents[$val[0]] as $next) {
            $sorted[] = $next;
        }
    }
}

此代码需要 PHP 7,在某些情况下它可能无法在 PHP 5 下运行。 - 为了与 PHP 5 兼容,您必须将 foreach ($sorted as &$val) 替换为 for ($val = reset($sorted); $val; $val = next($sorted)):

# a bit slower loop which works in all versions
for ($val = reset($sorted); $val; $val = next($sorted)) {
    if (isset($parents[$val[0]])) {
        foreach ($parents[$val[0]] as $next) {
            $sorted[] = $next;
        }
    }
}

现场演示:https://3v4l.org/Uk6Gs

【讨论】:

  • 我正在使用 PHP 5.6 - 你能突出显示你知道哪些不起作用的部分吗?
  • 当循环在最后一个元素处插入新元素时,循环将立即终止而不是继续。解决方法是将 foreach 更改为 for ($val = reset($sorted); $val; $val = next($sorted))。
  • 我在 $sorted[]=$next 行遇到内存不足错误
  • @bhttoan 抱歉,我在测试数据时使用了错误的索引。将 2 替换为 0;现在可以用了吗?
  • 内存错误消失但输出/排序数组为空
【解决方案2】:

我为你准备了两个不同的版本。

a) 使用带有递归和引用的“遍历树”方法来最小化内存消耗

$data = [
    [207306,'Bob',''], [199730,'Sam',199714],
    [199728,'Simon',207306], [199714,'John',207306],
    [199716, 'Tom',199718], [199718,'Phillip',207306],
    [199720,'James',207306]
];

$list = [];
generateList($data, '', $list);

var_dump($list);

function generateList($data, $id, &$list) {
    foreach($data as $d) {
        if($d[2] == $id) {          
            $list[] = $d; // Child found, add it to list            
            generateList($data, $d[0], $list); // Now search for childs of this child
        }
    }
}

b) 使用内置于 uusort()function 的 phps(似乎只适用于 php 5.x 而不适用于 php7+)

$data = [
    [207306,'Bob',''], [199730,'Sam',199714],
    [199728,'Simon',207306], [199714,'John',207306],
    [199716, 'Tom',199718], [199718,'Phillip',207306],
    [199720,'James',207306]
];

usort($data, 'cmp');

var_dump($data);

function cmp($a, $b) {  
    if($a[2] == '' || $a[0] == $b[2]) return -1; //$a is root element or $b is child of $a
    if($b[2] == '' || $b[0] == $a[2]) return 1; //$b is root element or $a is child of $b
    return 0; // both elements have no direct relation
}

【讨论】:

  • 尝试使用 PHP 7 运行 usort() 示例,它不会给出预期的结果 [由于算法更改]。 usort() 仅用于总排序(即每个元素之间都有明确定义的关系,显示出明确定义的顺序)。让我们以 $a、$b 和 $c 为例;那么如果 $a == $b 和 $b == $c,则假定 $a == $c。您的代码适用于 PHP 5 的此输入,但对于不同的输入,它在 PHP 5 上也会失败。
  • 感谢您的反馈。您能否提供一个 usort() 方法不起作用的输入数据示例(使用 php5)?
  • 感谢您的反馈,不知道usort()的行为从php5变成了php7。
【解决方案3】:

我检查了它在 PHP 5.6 和 PHP 7 中的工作原理

示例数组:

$array = Array(0 => Array(
        0 => 207306,
        1 => 'Bob',
        2 => '',
    ),
    1 => Array
        (
        0 => 199730,
        1 => 'Sam',
        2 => 199714,
    ),
    2 => Array
        (
        0 => 199728,
        1 => 'Simon',
        2 => 207306,
    ),
    3 => Array
        (
        0 => 199714,
        1 => 'John',
        2 => 207306,
    ),
    4 => Array
        (
        0 => 199716,
        1 => 'Tom',
        2 => 199718,
    ),
    5 => Array
        (
        0 => 199718,
        1 => 'Phillip',
        2 => 207306,
    ),
    6 => Array
        (
        0 => 199720,
        1 => 'James',
        2 => 207306,
    ),
); 



echo "<pre>";
$emp = array();

//form the array with parent and child
foreach ($array as $val) {
    $manager = ($val[2] == '') ? 0 : $val[2];
    $exist = array_search_key($val[2], $emp);
    if ($exist)
        $emp[$exist[0]][$val[0]] = $val;
    else
    //print_R(array_search_key(199714,$emp));
        $emp[$manager][$val[0]] = $val;
}

$u_emp = $emp[0];
unset($emp[0]);

//associate the correct child/emp after the manager
foreach ($emp as $k => $val) {
    $exist = array_search_key($k, $u_emp);
    $pos = array_search($k, array_keys($u_emp));

    $u_emp = array_slice($u_emp, 0, $pos+1, true) +
            $val +
            array_slice($u_emp, $pos-1, count($u_emp) - 1, true);

}
print_R($u_emp); //print the final result

// key search function from the array
function array_search_key($needle_key, $array, $parent = array())
{
    foreach ($array AS $key => $value) {
        $parent = array();
        if ($key == $needle_key)
            return $parent;
        if (is_array($value)) {
            array_push($parent, $key);
            if (($result = array_search_key($needle_key, $value, $parent)) !== false)
                return $parent;
        }
    }
    return false;
}

【讨论】:

    【解决方案4】:

    找到下面可能有用的代码。因此,您的输出存储在 $sortedarray 中。

    $a=array(array(207306,'Bob',''),
    array (199730,'Sam',199714),
    array(199728,'Simon',207306),
    array(199714,'John',207306),
    array(199716,'Tom',199718),
    array(199718,'Phillip',207306),
    array(199720,'James',207306));
    
    $sortedarray=$a;
    foreach($a as $key=>$value){
        $checkvalue=$value[2];
        $checkkey=$key;
    foreach($a as $key2=>$value2){
        if($key<$key2){
                if ($value2[0]===$checkvalue){
                    $sortedarray[$key]=$value2;
                    $sortedarray[$key2]=$value;
            }else{
                }
        }
      }
     }
    
     print_r($sortedarray);
    

    【讨论】:

      【解决方案5】:

      这种方法怎么样:

      创建一个空数组result。

      循环遍历您的数组,仅将其中 [2] 为空的项目 取出 插入到result。

      当这个循环完成后,你在while-loop 中使用foreach-Loop。使用foreach-Loop,您可以将数组中的每一项out 取出,其中[2] 已经是result 的一部分。只要您的数组包含任何内容,您就可以这样做。

      $result = array();
      $result[''] = 'root';
      
      while(!empty($yourArray)){
        foreach($yourArray as $i=>$value){
          if(isset($result[$value[2]])){
            // use the next line only to show old order
            $value['oldIndex'] = $i;
            $result[$value[0]] = $value;
            unset($yourArray[$i]);
          }
        }
      }
      
      unset($result['']);
      

      PS:您可能会在走过阵列时移除阵列的某些部分而遇到麻烦。如果你这样做...尝试解决这个问题:)

      PPS:如果您的数组有未解决的循环或没有父级的子级,请考虑中断条件。

      【讨论】:

        【解决方案6】:

        您可以在变量 $arr 中使用您的数组并使用此代码,它将为您提供所需的输出。

        function check($a, $b) {   
            return ($a[0] == $b[2]) ? -1 : 1;
        }
        
        
        uasort($arr, 'check');
        echo '<pre>';
        print_r(array_values($arr));
        echo '</pre>';
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-23
        • 1970-01-01
        • 2019-08-10
        • 2015-04-21
        • 2020-01-03
        • 1970-01-01
        • 1970-01-01
        • 2020-03-07
        相关资源
        最近更新 更多