Php 如何重新排列将依赖项移动到顶部的数组项?
我有以下Php 如何重新排列将依赖项移动到顶部的数组项?,php,arrays,algorithm,multidimensional-array,Php,Arrays,Algorithm,Multidimensional Array,我有以下数组,其中每个项目可能(也可能不依赖)另一个: $test=array( 'c'=>数组( '依赖'=>'b' ), 'a'=>array(), “b”=>数组( '取决于'=>'a' ), 'd'=>数组( '取决于'=>'a' ), ); 我想移动(或制作另一个数组),将依赖项移动到顶部。首先是a,然后是b和d(两者都取决于a),最后是c,后者取决于b。b和d的顺序是不相关的: $rearsed=数组( 'a'=>array(), “b”=>数组( '取决于'=>'a' ), 'd
数组
,其中每个项目可能(也可能不依赖)另一个:
$test=array(
'c'=>数组(
'依赖'=>'b'
),
'a'=>array(),
“b”=>数组(
'取决于'=>'a'
),
'd'=>数组(
'取决于'=>'a'
),
);
我想移动(或制作另一个数组
),将依赖项移动到顶部。首先是a
,然后是b
和d
(两者都取决于a
),最后是c
,后者取决于b
。b
和d
的顺序是不相关的:
$rearsed=数组(
'a'=>array(),
“b”=>数组(
'取决于'=>'a'
),
'd'=>数组(
'取决于'=>'a'
),
'c'=>数组(
'依赖'=>'b'
),
);
我很确定这是一种非常普遍的情况,在重新发明轮子之前,我想知道是否有任何数据结构可以为我完成这项工作
EDIT:忘了说应该检测循环引用(b
依赖于a
,不允许依赖于b
的循环引用)。这被称为循环引用。如果你把你的结构看作一个图,其中“A取决于B”等于从顶点B到顶点A的有向边,那么你应该做一个拓扑排序来得到你的答案。
拓扑排序的实现如下所示:
设图[n][n]是与数组对应的图(图[i][j]=1表示j依赖于i)开始吧
findis.t.income[i]==0,used[i]==false
ans.append(i)
对于每个j s.t.图[i][j]==1递减收入[j]
结束
<?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);
?>
经过测试并正常工作:
基本上,它循环遍历每个节点,从树的顶部检查该节点的深度,并将值附加到一个新数组$ar
。然后它使用array\u multisort()
根据存储在$ar
中的每个节点的深度级别对$test
进行排序
我想使用最基本的依赖性排序,并将其作为一个有效的解决方案。到目前为止,测试了多种变奏和作品声音
private function getSorted($type)
{
uasort($this->assets[$type], array($this, 'sortDependancies'));
return $this->assets[$type];
}
/**
* Sorting utility function via uasort from getSorted
*
* @returns sort order
*/
private function sortDependancies($a, $b)
{
if ( is_array($b['dependant']) && in_array($a['alias'], $b['dependant'])) return -1;
if ( $a['alias'] == $b['dependant'] ) return -1;
return 1;
}
我创建它是为了在js和css资产集合对象数组中使用
protected $assets = array(
'stylesheets' => array(
'main' => array(
'alias' => 'main',
'path' => 'somepath.less',
'dependant' => 'bootstrap'
),
'bootstrap' => (
'alias' => 'bootstrap',
'path' => 'bootstrap.css',
'dependant' => NULL
)
),
'javascripts' => array()
);
因此,我会在getSorted()中将$type ie称为'stylesheets'或'javascripts'
我还制作了它,以便它可以使用“dependent”项的数组而不是字符串来处理多个依赖项
如果这个上下文需要简化,请告诉我,毕竟我是在一个对象中创建的
干杯 我的灵感来自。
所以我做了一些自我算法
并对该类进行了测试:
<?php
namespace Lib\Sort;
/**
* Class TopTest
*
* @package Lib\Sort
*/
class TopTest extends \PHPUnit_Framework_TestCase
{
public function testBasicSorting()
{
$test = new Top();
$test->addNode('A');
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A', 'F'));
$test->addNode('F', array('A'));
$test->addNode('G');
$this->assertTrue($test->isModeSingleNonEdgeNode());
$actual = $test->getSortedNodes();
$expected = array('A', 'F', 'B', 'D', 'C', 'E', 'G');
$this->assertEquals($expected, $actual);
}
/**
* Test sorting of last node with many edges
*
* @throws Exception
*/
public function testLastNodeSorting()
{
$test = new Top();
$test->addNode('A', array());
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A'));
$test->addNode('F', array('A', 'E'));
$actual = $test->getSortedNodes();
$expected = array('A', 'E', 'F', 'B', 'D', 'C');
$this->assertEquals($actual, $expected);
}
/**
* Test sorting disabled mode "Single non-edge node"
*
* @throws Exception
*/
public function testDisabledSingleNonEdgesSorting()
{
$test = new Top();
$test->addNode('A');
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A'));
$test->addNode('F', array('A', 'E'));
$test->addNode('G');
$test->enableModeSingleNonEdgeNode(false);
$actual = $test->getSortedNodes();
$expected = array('A', 'G', 'E', 'F', 'B', 'D', 'C');
$this->assertEquals($actual, $expected);
}
/**
* Test exception for cyclic nodes
*
* @expectedException \Lib\Sort\Exception
* @expectedExceptionMessage Cyclic dependencies between E and F.
*/
public function testCyclicSortingFailure()
{
$test = new Top();
$test->addNode('A', array());
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A', 'F'));
$test->addNode('F', array('A', 'E'));
$test->getSortedNodes();
//expected an exception
}
/**
* Test exception for cyclic nodes
*
* @expectedException \Lib\Sort\Exception
* @expectedExceptionMessage Nodes already sorted.
*/
public function testAddNodeAfterSortingFailure()
{
$test = new Top();
$test->addNode('A', array());
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A'));
$test->addNode('F', array('A', 'E'));
$test->getSortedNodes();
$test->addNode('H', array('E'));
//expected an exception
}
}
听起来不错。PHP中有什么工具可以进行拓扑排序吗?@Gremo:我不擅长PHP,但很容易实现。我将添加实现描述。您能解释一下multisort如何与2个输入一起工作吗?你的解决方案看起来很简单…忘了说,循环引用(循环)会失败吗?这个解决方案使用了一个错误的算法,排序不能高于第二维度。我的算法能够在第二维度之外工作。@Gremo将我的答案编辑为循环参考。……多分类仅在一个输入下工作。。。。以及必须对数组进行排序的维度。。。。您希望我再编辑我的答案吗…?如果检测到循环引用,您希望代码如何响应?抛出致命错误?或者将这些节点放在列表的底部?@Ultimater致命错误,异常。。。不应被允许。@Gremo您想让我编辑大于第二维度的层次结构的答案吗?感谢您的回答(+1),但问题中暗示我只需要层次结构中的一个级别。现在编辑如果它根据您对我的问题的回答检测到循环引用,将抛出错误。不,不是。另一方面,你的问题中隐含着这样一个事实:基于这一部分,你的层次结构中需要不止一个层次:首先是a,然后是b和d(两者都依赖于a),最后是c,它依赖于b。b和d的顺序是不相关的
你最后说的是c,因为它取决于b,而b又取决于a。
/**
* Class Top
*
* @package Lib\Sort
*/
class Top
{
/**
* Unsorted nodes
*
* @var array
*/
protected $_nodes = array();
/**
* Nodes structure
*
* @var array
*/
protected $_structure = array();
/**
* Stored nodes
*
* @var array|null
*/
protected $_sortedNodes;
/**
* Stored nodes
*
* @var array
*/
protected $_level = 0;
/**
* Status of mode "single non-edge node"
*
* @var bool
* @see setModeSingleNonEdgeNode()
*/
protected $_singleNonEdgeNode = true;
/**
* Get status of "Single non-edge node" mode
*
* @return boolean
* @see setModeSingleNonEdgeNode()
*/
public function isModeSingleNonEdgeNode()
{
return $this->_singleNonEdgeNode;
}
/**
* Set status of "Single non-edge node" mode
*
* This status means that sorting will move only first non-edged node to top.
* Rest non-edge nodes will be added according to sorting in _nodes property
* In case it will FALSE all nodes will be moved to top.
*
* @param boolean $flag
* @return $this
*/
public function enableModeSingleNonEdgeNode($flag)
{
$this->_singleNonEdgeNode = (bool)$flag;
return $this;
}
/**
* Add node
*
* @param string $name
* @param array $dependsOn
* @throws Exception
* @return $this
*/
public function addNode($name, array $dependsOn = array())
{
if (null !== $this->_sortedNodes) {
throw new Exception('Nodes already sorted.');
}
$this->_nodes[$name] = $name;
$this->_structure[$name] = $dependsOn;
return $this;
}
/**
* Sort
*
* @return array
*/
public function getSortedNodes()
{
if (null === $this->_sortedNodes) {
$this->_sortedNodes = array();
//insert non-edged nodes
$this->_performNonEdgedNodes();
//insert edged nodes
$this->_performEdgedNodes();
}
return $this->_sortedNodes;
}
/**
* Move node into sorted list
*
* @param string $name
* @throws Exception
* @return $this
*/
protected function _moveNodeToSortedList($name)
{
$node = $this->_takeNode($name);
if ($node) {
$this->_sortedNodes[] = $node;
} else {
throw new Exception("The node '$name' has already been taken.");
}
return $this;
}
/**
* Take node from the list
*
* @param string $name
* @return string|null
*/
protected function _takeNode($name)
{
if (!isset($this->_nodes[$name])) {
return null;
}
$node = $this->_nodes[$name];
unset($this->_nodes[$name]);
return $node;
}
/**
* Perform node sorting
*
* @param string $name
* @return $this
* @throws Exception
*/
protected function _performNode($name)
{
$node = $this->_takeNode($name);
if (null === $node) {
return $this;
}
foreach ($this->_structure[$node] as $depNode) {
$this->_checkCycledEdges($node, $depNode);
$this->_performNode($depNode);
}
$this->_sortedNodes[] = $node;
return $this;
}
/**
* Check cycled edges
*
* @param string $node
* @param string $edgeNode
* @return bool
* @throws Exception
*/
protected function _checkCycledEdges($node, $edgeNode)
{
if (in_array($node, $this->_structure[$edgeNode])) {
throw new Exception("Cyclic dependencies between $edgeNode and $node.");
}
return true;
}
/**
* Perform edged nodes
*
* @return $this
*/
protected function _performEdgedNodes()
{
while (!empty($this->_nodes)) {
$name = current($this->_nodes);
$this->_performNode($name);
}
return $this;
}
/**
* Perform non-edged nodes
*
* @return $this
*/
protected function _performNonEdgedNodes()
{
foreach ($this->_structure as $name => $edges) {
if (!$edges) {
$this->_moveNodeToSortedList($name);
if ($this->isModeSingleNonEdgeNode()) {
//to add only first node and to add rest non-edge nodes according to sorting in _nodes property
break;
}
}
}
return $this;
}
}
<?php
namespace Lib\Sort;
/**
* Class TopTest
*
* @package Lib\Sort
*/
class TopTest extends \PHPUnit_Framework_TestCase
{
public function testBasicSorting()
{
$test = new Top();
$test->addNode('A');
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A', 'F'));
$test->addNode('F', array('A'));
$test->addNode('G');
$this->assertTrue($test->isModeSingleNonEdgeNode());
$actual = $test->getSortedNodes();
$expected = array('A', 'F', 'B', 'D', 'C', 'E', 'G');
$this->assertEquals($expected, $actual);
}
/**
* Test sorting of last node with many edges
*
* @throws Exception
*/
public function testLastNodeSorting()
{
$test = new Top();
$test->addNode('A', array());
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A'));
$test->addNode('F', array('A', 'E'));
$actual = $test->getSortedNodes();
$expected = array('A', 'E', 'F', 'B', 'D', 'C');
$this->assertEquals($actual, $expected);
}
/**
* Test sorting disabled mode "Single non-edge node"
*
* @throws Exception
*/
public function testDisabledSingleNonEdgesSorting()
{
$test = new Top();
$test->addNode('A');
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A'));
$test->addNode('F', array('A', 'E'));
$test->addNode('G');
$test->enableModeSingleNonEdgeNode(false);
$actual = $test->getSortedNodes();
$expected = array('A', 'G', 'E', 'F', 'B', 'D', 'C');
$this->assertEquals($actual, $expected);
}
/**
* Test exception for cyclic nodes
*
* @expectedException \Lib\Sort\Exception
* @expectedExceptionMessage Cyclic dependencies between E and F.
*/
public function testCyclicSortingFailure()
{
$test = new Top();
$test->addNode('A', array());
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A', 'F'));
$test->addNode('F', array('A', 'E'));
$test->getSortedNodes();
//expected an exception
}
/**
* Test exception for cyclic nodes
*
* @expectedException \Lib\Sort\Exception
* @expectedExceptionMessage Nodes already sorted.
*/
public function testAddNodeAfterSortingFailure()
{
$test = new Top();
$test->addNode('A', array());
$test->addNode('B', array('A', 'F'));
$test->addNode('C', array('B', 'D'));
$test->addNode('D', array('F'));
$test->addNode('E', array('A'));
$test->addNode('F', array('A', 'E'));
$test->getSortedNodes();
$test->addNode('H', array('E'));
//expected an exception
}
}