PHP usort用于字符串层次结构

时间:2019-01-25 06:15:09

标签: php sorting usort

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

strcpy(james.name, "james");

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

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

2 个答案:

答案 0 :(得分: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数组中,并带有no。它们发生在原始阵列中的次数。

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

第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递归地收集它们。这样,我们始终确保父母先于孩子。

  • 在上一节中,我们仅打印结果。

答案 1 :(得分:1)

从根本上讲,您需要递归处理数据以将树结构解析出并按顺序解析。该功能可以做到这一点。它查找当前父级的子级(通过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