I have the following array
where each item may (or may not depend) on another one:
$test = array(
'c' => array(
'depends' => 'b'
),
'a' => array(),
'b' => array(
'depends' => 'a'
),
'd' => array(
'depends' => 'a'
),
);
I want to move (or make another array
) with dependencies are moved at the top. First a
, then b
and d
(both depend on a
) and finally c
which depends on b
. The order of b
and d
is irrelevant:
$rearranged = array(
'a' => array(),
'b' => array(
'depends' => 'a'
),
'd' => array(
'depends' => 'a'
),
'c' => array(
'depends' => 'b'
),
);
I'm pretty sure that this is a quite common situation and before reinventing the wheel and waste time I'd like to know if there is any data structure that can do the job for me.
EDIT: forgot to say that circular references should be detected (b
depends on a
that depends on b
should not be allowed).
Approach used in the below program is as followsDeclare a variable as max_val and set it with arr[size - 1] + 1. Start loop FOR from i to 0 till i less than size. Inside the loop, check IF i % 2 = 0 then set arr[i] to arr[i] + (arr[max] % max_val) * max_val and decrement the max by 1.
This is called topological sorting. If you consider your structure as a graph, where "a depends on b" is equal to a directed edge from vertex b to vertex a, you should just do a topological sort to get your answer.
Implementation of topological sort can be done like this:
let graph[ n ][ n ] be the graph corresponding to your array (graph[ i ][ j ] = 1 means j depends on i).
array_multisort will help you here..
<?php
$test = array(
'c' => array(
'depends' => 'b'
),
'a' => array(),
'b' => array(
'depends' => 'a'
),
'd' => array(
'depends' => 'a'
)
);
function sortArray($array= array())
{
$depend = array();
foreach ($array as $key => $value)
{
if (isset($value['depends']))
{
$depend[$key] = $value['depends'];
}
else
{
$depend[$key] = null;
}
}
return $depend;
}
function findCircularReference($array)
{
foreach($array as $key=>$value)
{
if(isset($array[$value]) && ($array[$value] == $key))
{
return true;
}
}
return false;
}
$depend = sortArray($test);
$checkReference = findCircularReference($depend);
if( ! $checkReference)
{
array_multisort($depend, SORT_ASC, $test);
}
else
{
trigger_error("Circular reference", E_USER_ERROR);
}
print_r($test);
?>
Tested and works:
Basically it loops through each node and checks the depth of that node from the top of the tree and appends the value to a new array $ar
. Then it sorts $test
based on the depth level of each node stored in $ar
using array_multisort()
.
<?php
$test = array(
'c' => array(
'depends' => 'b'
),
'a' => array(),
'b' => array(
'depends' => 'a'
),
'd' => array(
'depends' => 'a'
),
);
function getNodeLevel($ar,$n,$ref=array())
{
if(!isset($ar[$n]['depends'])){return 0;}
if(in_array($n,$ref)){return -1;}
$ref[]=$n;
$r=getNodeLevel($ar,$ar[$n]['depends'],$ref);
return ($r==-1?-1:$r+1);
}
$ar=array();
foreach($test as $i=>$tmp)
{
$ar[]=getNodeLevel($test,$i);
}
if(!in_array(-1,$ar))
{
array_multisort($ar,SORT_ASC,$test);
print_r($test);
}else{
trigger_error("Circular reference detected.", E_USER_ERROR);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With