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)

  • ans={}//空序列
  • 收入=新数组[n]
  • 收入[i]=进入顶点i的边数
  • used=新建数组[n]//显示是否已使用任何顶点,默认为all false
  • ans.size!=n//仍有未使用的顶点
    开始吧
    findis.t.income[i]==0,used[i]==false
    ans.append(i)
    对于每个j s.t.图[i][j]==1递减收入[j]
    结束
  • 返回ans
  • 我会帮你的

    <?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
        }
    }