【问题标题】:PHP usort for string hierarchy用于字符串层次结构的 PHP 排序
【发布时间】:2019-06-18 23:15:14
【问题描述】:

我有一个字符串层次结构的数组,如下所示:

table, parent_table
test, NULL
test, NULL
test2, test
test4, NULL
test5, test3
test6, test5
test3, test

我想使用类似这样的函数进行排序:

usort($array, function($a,$b) {
    return ($a['table'] === $b['parent_table']) ? -1 : 1;
});

理想的结果是

table, parent_table
test, NULL
test, NULL
test2, test
test3, test
test5, test3
test6, test5
test4, NULL

这会将父母排序在子表之上。我一直在努力为这个问题找到一个好的解决方案。字符串层次结构是否有usort

【问题讨论】:

  • 为什么test是2次?
  • 这是数组的简化版本。该数组包含与其他变量不同的信息,这些变量与每一行分开但具有相同的表和父表。
  • 那么你如何区分一组test, NULL 和另一组的孩子呢?
  • 这就像有两个已婚并有孩子的父母。关键是让孩子出现在父母之后,不管父母是谁。

标签: php sorting usort


【解决方案1】:
<?php

$data = [
    [
        'table' => 'test',
        'parent_table' => NULL,
    ],
    [
        'table' => 'test',
        'parent_table' => NULL,
    ],
    [
        'table' => 'test2',
        'parent_table' => 'test',
    ],
    [
        'table' => 'test4',
        'parent_table' => NULL,
    ],
    [
        'table' => 'test5',
        'parent_table' => 'test3',
    ],
    [
        'table' => 'test6',
        'parent_table' => 'test5',
    ],
    [
        'table' => 'test3',
        'parent_table' => 'test',
    ],
];


function reorderHierarchy($data){

    $hierarchy = [];
    $top_level_parents = [];

    foreach($data as $each_data){
        $hierarchy[$each_data['table']] = array();
        if(is_null($each_data['parent_table'])){
            if(!isset($top_level_parents[$each_data['table']])){
                $top_level_parents[$each_data['table']] = 0;
            }
            $top_level_parents[$each_data['table']]++;
        }
    }

    foreach($data as $each_data){
        if(!is_null($each_data['parent_table'])){
            $hierarchy[$each_data['parent_table']][] = $each_data['table'];
        }
    }

    $result = [];
    traverseHierarchy($hierarchy,$top_level_parents,$result);
    return $result;
}

function traverseHierarchy($hierarchy,$top_level_parents,&$result){
    foreach($top_level_parents as $each_parent => $occurrences){
        while($occurrences-- > 0){
            $result[] = [
                'table' => $each_parent,
                'parent_table' => NULL
            ];
        }       

        traverseChildren($hierarchy,$each_parent,$result);
    }
}

function traverseChildren($hierarchy,$parent,&$result){
    foreach($hierarchy[$parent] as $each_child){
        $result[] = [
            'table' => $each_child,
            'parent_table' => $parent
        ];

        traverseChildren($hierarchy,$each_child,$result);
    }
}


foreach(reorderHierarchy($data) as $each_data){
    echo $each_data['table']," , ",(is_null($each_data['parent_table']) ? "NULL" : $each_data['parent_table']),"<br/>";
}

输出:

test , NULL
test , NULL
test2 , test
test3 , test
test5 , test3
test6 , test5
test4 , NULL

演示: https://3v4l.org/AmJpY

说明:

第 1 部分:

function reorderHierarchy($data){

    $hierarchy = [];
    $top_level_parents = [];

    foreach($data as $each_data){
        $hierarchy[$each_data['table']] = array();
        if(is_null($each_data['parent_table'])){
            if(!isset($top_level_parents[$each_data['table']])){
                $top_level_parents[$each_data['table']] = 0;
            }
            $top_level_parents[$each_data['table']]++;
        }
    }

    foreach($data as $each_data){
        if(!is_null($each_data['parent_table'])){
            $hierarchy[$each_data['parent_table']][] = $each_data['table'];
        }
    }

    $result = [];
    traverseHierarchy($hierarchy,$top_level_parents,$result);
    return $result;
}
  • 在上面的函数中,我们创建了2种数组,分别是$hierarchy$top_level_parents$hierarchy 是一个数组,其中每个键都有它的子键。 $top_level_parents 是一个数组,它收集所有没有任何父表的表,键是表名,值是它的出现次数。

  • 然后我们调用另一个函数traverseHierarchy 来遍历所有这些顶级父母并获取他们的孩子。这样,我们将始终先访问父母,然后再访问孩子,因为我们首先迭代父母(甚至对于将依次成为其他表的父母的孩子也是有效的)。

  • 为了更好地解释,两个数组如下所示:

$层次结构:

Array
(
    [test] => Array
        (
            [0] => test2
            [1] => test3
        )

    [test2] => Array
        (
        )

    [test4] => Array
        (
        )

    [test5] => Array
        (
            [0] => test6
        )

    [test6] => Array
        (
        )

    [test3] => Array
        (
            [0] => test5
        )
)

$top_level_parents:

Array
(
    [test] => 2
    [test4] => 1
)

第 2 部分:

function traverseHierarchy($hierarchy,$top_level_parents,&$result){
    foreach($top_level_parents as $each_parent => $occurrences){
        while($occurrences-- > 0){
            $result[] = [
                'table' => $each_parent,
                'parent_table' => NULL
            ];
        }       

        traverseChildren($hierarchy,$each_parent,$result);
    }
}
  • 在这里,我们遍历所有顶级父级,将它们存储在result 数组中,编号为。它们在原始数组中出现的次数。

  • 完成后,我们现在将遍历它的子节点,并将它的所有子节点也包含在结果数组中。

第 3 部分:

function traverseChildren($hierarchy,$parent,&$result){
    foreach($hierarchy[$parent] as $each_child){
        $result[] = [
            'table' => $each_child,
            'parent_table' => $parent
        ];

        traverseChildren($hierarchy,$each_child,$result);
    }
}
  • 在这里,我们遍历所有子节点并将它们包含在 result 中。这个孩子很可能也有自己的孩子。因此,我们将使用depth first search 递归地收集它们。这样,我们始终确保父母先于孩子。

  • 在最后一节中,我们只打印结果。

【讨论】:

    【解决方案2】:

    从根本上说,您需要递归地处理数据以将树结构解析出来并按顺序排列。这个函数会做到这一点。它查找当前父项的子项(通过array_filter 选择它们),然后遍历当前子项,将其所有子项合并到输出数组中。由于需要跳过匹配的父母,我们必须在将其添加到输出之前检查一个孩子是否与前一个不同:

    function hsort($array, $parent) {
        $output = array();
        $children = array_filter($array, function ($v) use ($parent) { return $v['parent_table'] === $parent; });
        sort($children);
        $lastchild = NULL;
        foreach ($children as $child) {
            if ($child != $lastchild && !is_null($lastchild)) {
                $output[] = $lastchild;
                $output = array_merge($output, hsort($array, $lastchild['table']));
            }
            elseif ($lastchild != NULL) {
                $output[] = $lastchild;
            }
            $lastchild = $child;
        }
        if (!is_null($lastchild)) {
            $output[] = $lastchild;
            $output = array_merge($output, hsort($array, $lastchild['table']));
        }
        return $output;
    }
    
    echo "table   | parent_table\n";
    foreach (hsort($array, NULL) as $v) {
        printf("%-8s| %s\n", $v['table'], $v['parent_table'] ?? 'NULL');
    }
    

    输出

    table | parent_table 
    test  | NULL
    test  | NULL
    test2 | test 
    test3 | test 
    test5 | test3 
    test6 | test5 
    test4 | NULL
    

    Demo on 3v4l.org

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      相关资源
      最近更新 更多