根据父/子关系对数组值进行排序

时间:2016-08-24 11:11:07

标签: php arrays sorting

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

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个级别。

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

6 个答案:

答案 0 :(得分:5)

这将简单地命令数组(在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

答案 1 :(得分: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)使用内置u usort()函数的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
}

答案 2 :(得分:1)

我检查过这适用于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;
}

答案 3 :(得分:1)

找到下面可能有用的代码。所以,你的输出存储在$ 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);

答案 4 :(得分:0)

这种方法怎么样:

创建一个空数组string_facet_field

在你的数组上循环,只取[2]为空的 out 项,然后将它们插入result

完成此循环后,在result循环内使用foreach - 循环。使用while - 循环,您可以获取数组 out 的每个项目,其中[2]已经是foreach的一部分。只要您的数组包含任何内容,就可以执行此操作。

result

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

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

答案 5 :(得分:0)

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

function check($a, $b) {   
    return ($a[0] == $b[2]) ? -1 : 1;
}


uasort($arr, 'check');
echo '<pre>';
print_r(array_values($arr));
echo '</pre>';