嵌套平面列表
Nesting a flat list
我几乎一整天都在尝试将这个平面类别列表转换为嵌套列表,因为它应该是一个 5 级深类别列表。
这是一个例子:
$array =
array (
array (
'CategoryID' => '001',
'CategoryLevel' => '1',
'CategoryName' => 'Category 1',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '002',
'CategoryLevel' => '2',
'CategoryName' => 'Category 2',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '003',
'CategoryLevel' => '3',
'CategoryName' => 'Category 3',
'CategoryParentID' => '002'
),
array (
'CategoryID' => '004',
'CategoryLevel' => '4',
'CategoryName' => 'Category 4',
'CategoryParentID' => '003'
),
array (
'CategoryID' => '005',
'CategoryLevel' => '5',
'CategoryName' => 'Category 5',
'CategoryParentID' => '004'
)
);
我需要知道我应该如何更改这个数组,这样我就可以将它转换成一个文本文件,每个子类别由 tab
指定。
Here's what I've been doing, and here's 我正在处理的实际数组。
$categories = array();
foreach ($array['CategoryArray']['Category'] as $category) {
//echo print_r($category,1);
echo 'category parent ID: '.$category['CategoryParentID']."\n";
$array_search = recursive_array_search($category['CategoryParentID'], $categories);
echo 'Array search: '.$array_search."\n";
$index = isset($array_search) ? $array_search : false;
echo 'index: '.print_r($index)."\n";
if ($index) {
//echo "\n\n=================".
$category['CategoryID'].
' - '.
$category['CategoryName'].
' (PARENT: '. $category['CategoryParentID'].
') =================';
$value = eval('$categories'.$index);
$value[$category['CategoryID']] = $category['CategoryName'];
}
else {
//echo "\n\n=================".$category['CategoryID'].' - '.$category['CategoryName'].'=================';
$categories[$category['CategoryParentID']][$category['CategoryID']] = $category['CategoryName'];
}
}
我想有问题的部分是(重新?)构建层次结构,但是一旦你有了缩进输出就不是问题了。
还没有测试过(边缘情况)——但我相当有信心....
<?php
$h = foo( getData() ) ;
var_export($h);
/* the assumption here is:
when iterating $src the parent elements are always iterated before their respective child elements
reverse the array and the opposite is true: the child element will always be iterated first
-> ALL child elements are "waiting" for their respective parent element to "collect" them
<-> when the parent element is iterated all its children have already been iterated
Once an element is iterated it therefore
- looks for its (previously iterated and stored) childnodes
- "registers" itself in that storage array
- unless its a root element (parentid==ownid), in which case it's registered as root element
*/
function foo(array $src) {
$src = array_reverse($src);
$toBeCollected = array(); // all the children waiting for their parents
$retval = array(); // array of root elements
foreach( $src as $e ) {
// first we collect the children (if there are any)
if ( isset($toBeCollected[ $e['CategoryID'] ]) ) {
$e['childnodes'] = $toBeCollected[ $e['CategoryID'] ];
unset($toBeCollected[ $e['CategoryID'] ]);
}
if ( $e['CategoryID']===$e['CategoryParentID'] ) {
// it's a root element
$retval[ $e['CategoryID'] ] = $e;
}
else {
setEntry($toBeCollected, $e['CategoryParentID'], $e);
}
}
return $retval;
}
function setEntry(array &$target, $key, $value) {
if ( isset($target[$key]) ) {
$target[$key][] = $value;
}
else {
$target[$key] = array($value);
}
}
function getData() {
return array (
array (
'CategoryID' => '001',
'CategoryLevel' => '1',
'CategoryName' => 'Category 1',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '002',
'CategoryLevel' => '2',
'CategoryName' => 'Category 2',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '003',
'CategoryLevel' => '3',
'CategoryName' => 'Category 3',
'CategoryParentID' => '002'
),
array (
'CategoryID' => '004',
'CategoryLevel' => '4',
'CategoryName' => 'Category 4',
'CategoryParentID' => '003'
),
array (
'CategoryID' => '005',
'CategoryLevel' => '5',
'CategoryName' => 'Category 5',
'CategoryParentID' => '004'
),
array (
'CategoryID' => '006',
'CategoryLevel' => '2',
'CategoryName' => 'Category 6',
'CategoryParentID' => '001'
)
);
}
编辑:没有临时幼儿园也是一样
<?php
$h = foo( getData() ) ;
var_export($h);
/* the assumption here is:
when iterating $src the parent elements are always iterated before their respective child elements
reverse the array and the opposite is true: the child element will always be iterated first
-> ALL child elements are "waiting" for their respective parent element to "collect" them
<-> when the parent element is iterated all its children have already been iterated
Once an element is iterated it therefore
looks for its (previously iterated and stored) childnodes
"registers" itself in that storage array
unless its a root element (parentid==ownid), in which case it's registered as root element
*/
function foo(array $src) {
$src = array_reverse($src);
$nodes = array();
foreach( $src as $e ) {
// if there were child nodes "they" already created an entry in $nodes for this/current node
if ( isset($nodes[$e['CategoryID']]) ) {
// in this case the current node just "imports" the childnodes
$e['childnodes'] = $nodes[$e['CategoryID']]['childnodes'];
// "imports" means: node is moved, means: unset() on previous location
unset($nodes[$e['CategoryID']]);
}
// now we check if the current node has a parent or is a root
if ( $e['CategoryID']===$e['CategoryParentID'] ) {
// it's a root -> gets stored under its own id
$nodes[ $e['CategoryID'] ] = $e;
}
// it's a childnode -> create/append to stub for parent
else if ( isset($nodes[ $e['CategoryParentID'] ]) ) {
// there already was another child -> append to stub
$nodes[ $e['CategoryParentID'] ]['childnodes'][] = $e;
}
else {
// first child visited -> create stub
$nodes[ $e['CategoryParentID'] ] = array('childnodes' =>array($e));
}
}
return $nodes;
}
function getData() {
return array (
array (
'CategoryID' => '001',
'CategoryLevel' => '1',
'CategoryName' => 'Category 1',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '002',
'CategoryLevel' => '2',
'CategoryName' => 'Category 2',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '003',
'CategoryLevel' => '3',
'CategoryName' => 'Category 3',
'CategoryParentID' => '002'
),
array (
'CategoryID' => '004',
'CategoryLevel' => '4',
'CategoryName' => 'Category 4',
'CategoryParentID' => '003'
),
array (
'CategoryID' => '005',
'CategoryLevel' => '5',
'CategoryName' => 'Category 5',
'CategoryParentID' => '004'
),
array (
'CategoryID' => '006',
'CategoryLevel' => '2',
'CategoryName' => 'Category 6',
'CategoryParentID' => '001'
)
);
}
我几乎一整天都在尝试将这个平面类别列表转换为嵌套列表,因为它应该是一个 5 级深类别列表。
这是一个例子:
$array =
array (
array (
'CategoryID' => '001',
'CategoryLevel' => '1',
'CategoryName' => 'Category 1',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '002',
'CategoryLevel' => '2',
'CategoryName' => 'Category 2',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '003',
'CategoryLevel' => '3',
'CategoryName' => 'Category 3',
'CategoryParentID' => '002'
),
array (
'CategoryID' => '004',
'CategoryLevel' => '4',
'CategoryName' => 'Category 4',
'CategoryParentID' => '003'
),
array (
'CategoryID' => '005',
'CategoryLevel' => '5',
'CategoryName' => 'Category 5',
'CategoryParentID' => '004'
)
);
我需要知道我应该如何更改这个数组,这样我就可以将它转换成一个文本文件,每个子类别由 tab
指定。
Here's what I've been doing, and here's 我正在处理的实际数组。
$categories = array();
foreach ($array['CategoryArray']['Category'] as $category) {
//echo print_r($category,1);
echo 'category parent ID: '.$category['CategoryParentID']."\n";
$array_search = recursive_array_search($category['CategoryParentID'], $categories);
echo 'Array search: '.$array_search."\n";
$index = isset($array_search) ? $array_search : false;
echo 'index: '.print_r($index)."\n";
if ($index) {
//echo "\n\n=================".
$category['CategoryID'].
' - '.
$category['CategoryName'].
' (PARENT: '. $category['CategoryParentID'].
') =================';
$value = eval('$categories'.$index);
$value[$category['CategoryID']] = $category['CategoryName'];
}
else {
//echo "\n\n=================".$category['CategoryID'].' - '.$category['CategoryName'].'=================';
$categories[$category['CategoryParentID']][$category['CategoryID']] = $category['CategoryName'];
}
}
我想有问题的部分是(重新?)构建层次结构,但是一旦你有了缩进输出就不是问题了。
还没有测试过(边缘情况)——但我相当有信心....
<?php
$h = foo( getData() ) ;
var_export($h);
/* the assumption here is:
when iterating $src the parent elements are always iterated before their respective child elements
reverse the array and the opposite is true: the child element will always be iterated first
-> ALL child elements are "waiting" for their respective parent element to "collect" them
<-> when the parent element is iterated all its children have already been iterated
Once an element is iterated it therefore
- looks for its (previously iterated and stored) childnodes
- "registers" itself in that storage array
- unless its a root element (parentid==ownid), in which case it's registered as root element
*/
function foo(array $src) {
$src = array_reverse($src);
$toBeCollected = array(); // all the children waiting for their parents
$retval = array(); // array of root elements
foreach( $src as $e ) {
// first we collect the children (if there are any)
if ( isset($toBeCollected[ $e['CategoryID'] ]) ) {
$e['childnodes'] = $toBeCollected[ $e['CategoryID'] ];
unset($toBeCollected[ $e['CategoryID'] ]);
}
if ( $e['CategoryID']===$e['CategoryParentID'] ) {
// it's a root element
$retval[ $e['CategoryID'] ] = $e;
}
else {
setEntry($toBeCollected, $e['CategoryParentID'], $e);
}
}
return $retval;
}
function setEntry(array &$target, $key, $value) {
if ( isset($target[$key]) ) {
$target[$key][] = $value;
}
else {
$target[$key] = array($value);
}
}
function getData() {
return array (
array (
'CategoryID' => '001',
'CategoryLevel' => '1',
'CategoryName' => 'Category 1',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '002',
'CategoryLevel' => '2',
'CategoryName' => 'Category 2',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '003',
'CategoryLevel' => '3',
'CategoryName' => 'Category 3',
'CategoryParentID' => '002'
),
array (
'CategoryID' => '004',
'CategoryLevel' => '4',
'CategoryName' => 'Category 4',
'CategoryParentID' => '003'
),
array (
'CategoryID' => '005',
'CategoryLevel' => '5',
'CategoryName' => 'Category 5',
'CategoryParentID' => '004'
),
array (
'CategoryID' => '006',
'CategoryLevel' => '2',
'CategoryName' => 'Category 6',
'CategoryParentID' => '001'
)
);
}
编辑:没有临时幼儿园也是一样
<?php
$h = foo( getData() ) ;
var_export($h);
/* the assumption here is:
when iterating $src the parent elements are always iterated before their respective child elements
reverse the array and the opposite is true: the child element will always be iterated first
-> ALL child elements are "waiting" for their respective parent element to "collect" them
<-> when the parent element is iterated all its children have already been iterated
Once an element is iterated it therefore
looks for its (previously iterated and stored) childnodes
"registers" itself in that storage array
unless its a root element (parentid==ownid), in which case it's registered as root element
*/
function foo(array $src) {
$src = array_reverse($src);
$nodes = array();
foreach( $src as $e ) {
// if there were child nodes "they" already created an entry in $nodes for this/current node
if ( isset($nodes[$e['CategoryID']]) ) {
// in this case the current node just "imports" the childnodes
$e['childnodes'] = $nodes[$e['CategoryID']]['childnodes'];
// "imports" means: node is moved, means: unset() on previous location
unset($nodes[$e['CategoryID']]);
}
// now we check if the current node has a parent or is a root
if ( $e['CategoryID']===$e['CategoryParentID'] ) {
// it's a root -> gets stored under its own id
$nodes[ $e['CategoryID'] ] = $e;
}
// it's a childnode -> create/append to stub for parent
else if ( isset($nodes[ $e['CategoryParentID'] ]) ) {
// there already was another child -> append to stub
$nodes[ $e['CategoryParentID'] ]['childnodes'][] = $e;
}
else {
// first child visited -> create stub
$nodes[ $e['CategoryParentID'] ] = array('childnodes' =>array($e));
}
}
return $nodes;
}
function getData() {
return array (
array (
'CategoryID' => '001',
'CategoryLevel' => '1',
'CategoryName' => 'Category 1',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '002',
'CategoryLevel' => '2',
'CategoryName' => 'Category 2',
'CategoryParentID' => '001'
),
array (
'CategoryID' => '003',
'CategoryLevel' => '3',
'CategoryName' => 'Category 3',
'CategoryParentID' => '002'
),
array (
'CategoryID' => '004',
'CategoryLevel' => '4',
'CategoryName' => 'Category 4',
'CategoryParentID' => '003'
),
array (
'CategoryID' => '005',
'CategoryLevel' => '5',
'CategoryName' => 'Category 5',
'CategoryParentID' => '004'
),
array (
'CategoryID' => '006',
'CategoryLevel' => '2',
'CategoryName' => 'Category 6',
'CategoryParentID' => '001'
)
);
}