所以我在php中有一个项目数组,有些可能通过parent_id键链接到其他项目。我正在寻找这个数组的排序,以便其父项在此数组中的任何项目最终位于父项目的正下方。
示例:(实际数组有更多键)
some_array[0]['id'] = 15001;
some_array[0]['parent_id'] = 14899;
some_array[1]['id'] = 14723;
some_array[1]['parent_id'] = 0; //parent_id of 0 means item has no parent of its own
some_array[2]['id'] = 14899;
some_array[2]['parent_id'] = 0;
some_array[3]['id'] = 15000;
some_array[3][parent_id'] = 14723;
我想对这些进行排序,以便按顺序排列:
some_array[0]['id'] = 14723;
some_array[1]['id'] = 15000;
some_array[2]['id'] = 14899;
some_array[3]['id'] = 15001;
即。物品就在父母的下方。
提前致谢!
答案 0 :(得分:5)
我怀疑你们还在寻找一个真正的答案,但它可能会帮助其他人解决同样的问题。下面是一个递归函数,用于使用将子项放在父母下面的数组。
$initial = array(
array(
'name' => 'People',
'ID' => 2,
'parent' => 0
),
array(
'name' => 'Paul',
'ID' => 4,
'parent' => 2
),
array(
'name' => 'Liz',
'ID' => 5,
'parent' => 2
),
array(
'name' => 'Comus',
'ID' => 6,
'parent' => 3
),
array(
'name' => 'Mai',
'ID' => 7,
'parent' => 2
),
array(
'name' => 'Titus',
'ID' => 8,
'parent' => 3
),
array(
'name' => 'Adult',
'ID' => 9,
'parent' => 6
),
array(
'name' => 'Puppy',
'ID' => 10,
'parent' => 8
),
array(
'name' => 'Programmers',
'ID' => 11,
'parent' => 4
) ,
array(
'name' => 'Animals',
'ID' => 3,
'parent' => 0
)
);
/*---------------------------------
function parentChildSort_r
$idField = The item's ID identifier (required)
$parentField = The item's parent identifier (required)
$els = The array (required)
$parentID = The parent ID for which to sort (internal)
$result = The result set (internal)
$depth = The depth (internal)
----------------------------------*/
function parentChildSort_r($idField, $parentField, $els, $parentID = 0, &$result = array(), &$depth = 0){
foreach ($els as $key => $value):
if ($value[$parentField] == $parentID){
$value['depth'] = $depth;
array_push($result, $value);
unset($els[$key]);
$oldParent = $parentID;
$parentID = $value[$idField];
$depth++;
parentChildSort_r($idField,$parentField, $els, $parentID, $result, $depth);
$parentID = $oldParent;
$depth--;
}
endforeach;
return $result;
}
$result = parentChildSort_r('ID','parent',$initial);
print '<pre>';
print_r($result);
print '</pre>';
这是一种减速方法,可以从原始数组中删除元素,并按正确的顺序将它们放入结果集中。我为你做了一些通用的,所以它只需要你告诉它你的'ID'字段和'parent'字段被调用。顶级项目需要将parent_id(但是您将其命名为)为0。
答案 1 :(得分:4)
我的较短版本的matwang的回答:
/**
* sort parents before children
*
* @param array $objects input objects with attributes 'id' and 'parent'
* @param array $result (optional, reference) internal
* @param integer $parent (optional) internal
* @param integer $depth (optional) internal
* @return array output
*/
function parent_sort(array $objects, array &$result=array(), $parent=0, $depth=0) {
foreach ($objects as $key => $object) {
if ($object->parent == $parent) {
$object->depth = $depth;
array_push($result, $object);
unset($objects[$key]);
parent_sort($objects, $result, $object->id, $depth + 1);
}
}
return $result;
}
唯一的区别是它对一个对象数组进行排序,而不是对数组数组进行排序。
答案 2 :(得分:3)
您可以使用usort
按用户定义的功能排序:
function cmp($a, $b)
{
if ( $a['id'] == $b['id'] ) {
return 0;
} else if ( $a['parent_id'] ) {
if ( $a['parent_id'] == $b['parent_id'] ) {
return ( $a['id'] < $b['id'] ? -1 : 1 );
} else {
return ( $a['parent_id'] >= $b['id'] ? 1 : -1 );
}
} else if ( $b['parent_id'] ) {
return ( $b['parent_id'] >= $a['id'] ? -1 : 1);
} else {
return ( $a['id'] < $b['id'] ? -1 : 1 );
}
}
usort($some_array, "cmp");
注意:这只适用于深度为一级的树(意味着没有孩子的孩子)。对于更复杂的树,您可能希望将数据排序为图形,然后将其展平。
已修复 修改 以编辑$b
有父级但$a
没有父级的情况。
答案 3 :(得分:0)
只需使用usort()
功能,并以您需要的方式比较“大数组”的两个不同元素。这成了一个关于'我如何确定哪个元素在哪个元素之前的问题?'。
答案 4 :(得分:0)
如果您想支持多个子层,那么简单的用户将无效。没有其他信息,根本无法知道两个任意元素的比较。
我没有想太多,所以也许它不起作用。但这是一个排序类:
class TopSort
{
private $sorted, $unsorted;
private $history;
public function sort(array $unsorted)
{
$this->sorted = array();
$this->unsorted = $unsorted;
$this->history = array();
usort($this->unsorted, function($a, $b)
{
return $b['id'] - $a['id'];
});
foreach ($this->unsorted as $i => $a)
if ($a['parent_id'] == 0) $this->visit($i);
return array_reverse($this->sorted);
}
private function visit($i)
{
if (!array_key_exists($i, $this->history))
{
$this->history[$i] = true;
foreach ($this->unsorted as $j => $a)
if ($a['parent_id'] == $this->unsorted[$i]['id']) $this->visit($j);
$this->sorted[] = $this->unsorted[$i];
}
}
}
$sorter = new TopSort();
$some_array = $sorter->sort($some_array);
这里的想法是首先按id反向排序。然后通过首先插入最深的元素(没有子元素)来构建一个新数组。因为我们最初通过反向id对数组进行排序,所以它应该意味着整个事物是颠倒的。反转阵列后,应该完全符合您的要求。 (当然可以将数据移到数组上以避免反向操作,但这可能会更慢......)
这是非常未经优化,因为它遍历整个阵列很多次。只需稍加返工,就不需要这样做了。
这是一个更优化的替代类:
class TopSort
{
private $sorted;
public function sort(array $nodes)
{
$this->sorted = array();
# sort by id
usort($nodes, function($a, $b) {
return $a['id'] - $b['id'];
});
# build tree
$p = array(0 => array());
foreach($nodes as $n)
{
$pid = $n['parent_id'];
$id = $n['id'];
if (!isset($p[$pid]))
$p[$pid] = array('child' => array());
if (isset($p[$id]))
$child = &$p[$id]['child'];
else
$child = array();
$p[$id] = $n;
$p[$id]['child'] = &$child;
unset($child);
$p[$pid]['child'][] = &$p[$id];
}
$nodes = $p['0']['child'];
unset($p);
# flatten array
foreach ($nodes as $node)
$this->flatten($node);
return $this->sorted;
}
private function flatten(array $node)
{
$children = $node['child'];
unset($node['child']);
$this->sorted[] = $node;
foreach ($children as $node)
$this->flatten($node);
}
}
$sorter = new TopSort();
$sorted = $sorter->sort($some_array);
这是一个三步法:
根据id的预先分类,每组儿童都应该正确分类。