dog*_*c69 3 php multidimensional-array
我想将下面的文本转换为嵌套数组,就像你将使用MPTT数据库结构一样.
我从shell脚本获取数据,需要在网站上显示它.无法控制格式:/
有很多关于array - > list的信息,但是没有太多信息.
任何意见都将不胜感激,谢谢.
cat, true cat
=> domestic cat, house cat, Felis domesticus, Felis catus
=> kitty, kitty-cat, puss
=> mouser
=> alley cat
=> tom, tomcat
=> gib
=> Angora, Angora cat
=> Siamese cat, Siamese
=> blue point Siamese
=> wildcat
=> sand cat
=> European wildcat, catamountain, Felis silvestris
=> cougar, puma, catamount, mountain lion, painter, panther, Felis concolor
=> ocelot, panther cat, Felis pardalis
=> manul, Pallas's cat, Felis manul
=> lynx, catamount
=> common lynx, Lynx lynx
=> Canada lynx, Lynx canadensis
Run Code Online (Sandbox Code Playgroud)
你已经有了一个排序树列表.每一行都是前一行的子项或兄弟.因此,您可以处理列表,获取项目的名称,通过缩进获得项目所在的级别,并从中创建一个元素.
1 Line <=> 1 Element (level, name)
Run Code Online (Sandbox Code Playgroud)
所以每个元素都有一个名字,零个或多个孩子.从输入中也可以说它属于哪个级别.
元素可以表示为数组,其中第一个值是名称,第二个值是子元素的数组.
当列表被排序时,我们可以使用一个简单的映射,每个级别是某个级别的子级别的别名.因此,对于每个元素具有的级别,我们可以将其添加到堆栈中:
$self = array($element, array());
$stack[$level][] = &$self;
$stack[$level + 1] = &$self[1];
Run Code Online (Sandbox Code Playgroud)
正如此代码示例所示,当前级别的堆栈/映射$self随着子级添加而变化:
$stack[$level][] = &$self;
Run Code Online (Sandbox Code Playgroud)
一级更高的堆栈,得到$self(index 1)子项的引用:
$stack[$level + 1] = &$self[1];
Run Code Online (Sandbox Code Playgroud)
所以现在每行,我们需要找到水平.正如此堆栈所示,级别按顺序编号:0, 1, 2, ...但在输入中,它只是一些空格.
一个小帮助对象可以完成工作来收集/分组字符串中的字符数到级别,注意 - 如果缩进不存在级别 - 它会被添加,但只有在更高的情况下才会添加.
这解决了在输入中缩进的大小与其索引之间没有1:1关系的问题.至少不是一个明显的.
此帮助程序对象是示例性命名Levels并实现__invoke为缩进提供级别,同时在必要时透明地添加新级别:
$levels = new Levels();
echo $levels(''); # 0
echo $levels(' '); # 1
echo $levels(' '); # 1
echo $levels(' '); # 2
echo $levels(' '); # Throws Exception, this is smaller than the highest one
Run Code Online (Sandbox Code Playgroud)
所以现在我们可以将缩进变成这个级别.该级别允许我们运行堆栈.堆栈允许构建树.精细.
逐行解析可以使用正则表达式轻松完成.因为我很懒,我只是使用preg_match_all并返回 - 每行 - 缩进和名称.因为我想要更多的安慰,我将它包装成一个总是返回一个数组的函数,所以我可以在迭代器中使用它:
$matches = function($string, $pattern)
{
return preg_match_all($pattern, $string, $matches, PREG_SET_ORDER)
? $matches : array();
};
Run Code Online (Sandbox Code Playgroud)
在输入上使用类似的模式
/^(?:(\s*)=> )?(.*)$/m
Run Code Online (Sandbox Code Playgroud)
每行会给我一个数组,即:
array(whole_line, indent, name)
Run Code Online (Sandbox Code Playgroud)
你看到这里的模式?它接近
1 Line <=> 1 Element (level, name)
Run Code Online (Sandbox Code Playgroud)
在Levels对象的帮助下,这可以映射,所以只需要调用映射函数:
function (array $match) use ($levels) {
list(, $indent, $name) = $match;
$level = $levels($indent);
return array($level, $name);
};
Run Code Online (Sandbox Code Playgroud)
从array(line, indent, name)到array(level, name).要使其Levels可访问,可以通过另一个可以注入的函数返回:
$map = function(Levels $levels) {
return function ...
};
$map = $map(new Levels());
Run Code Online (Sandbox Code Playgroud)
所以,一切都是为了从各行读取.但是,这需要放在树中.记住添加到堆栈:
function($level, $element) use (&$stack) {
$self = array($element, array());
$stack[$level][] = &$self;
$stack[$level + 1] = &$self[1];
};
Run Code Online (Sandbox Code Playgroud)
($element这里是名字).这实际上需要堆栈,堆栈实际上是树.所以让我们创建另一个返回此函数的函数,并允许将每一行推入堆栈以构建树:
$tree = array();
$stack = function(array &$tree) {
$stack[] = &$tree;
return function($level, $element) use (&$stack) {
$self = array($element, array());
$stack[$level][] = &$self;
$stack[$level + 1] = &$self[1];
};
};
$push = $stack($tree);
Run Code Online (Sandbox Code Playgroud)
所以最后要做的就是一个接一个地处理另一个元素:
foreach ($matches($input, '/^(?:(\s*)=> )?(.*)$/m') as $match) {
list($level, $element) = $map($match);
$push($level, $element);
}
Run Code Online (Sandbox Code Playgroud)
所以现在有了$input这个创建一个数组,只有(根)子节点在它的第一级,然后array每个节点有两个条目:
array(name, children)
Run Code Online (Sandbox Code Playgroud)
Name在这里是一个字符串,children是一个数组.所以这已经在技术上完成了数组/树的列表.但它相当繁琐,因为您希望能够输出树结构.您可以通过执行递归函数调用或通过实现递归迭代器来实现.
让我给出一个Recursive Iterator示例:
class TreeIterator extends ArrayIterator implements RecursiveIterator
{
private $current;
public function __construct($node)
{
parent::__construct($node);
}
public function current()
{
$this->current = parent::current();
return $this->current[0];
}
public function hasChildren()
{
return !empty($this->current[1]);
}
public function getChildren()
{
return new self($this->current[1]);
}
}
Run Code Online (Sandbox Code Playgroud)
这只是一个数组迭代器(因为所有节点都是一个数组,以及所有子节点),对于当前节点,它返回名称.如果要求孩子,它会检查是否有孩子,并再次提供给孩子TreeIterator.这使得使用它变得简单,例如输出为文本:
$treeIterator = new RecursiveTreeIterator(
new TreeIterator($tree));
foreach ($treeIterator as $val) echo $val, "\n";
Run Code Online (Sandbox Code Playgroud)
输出:
\-cat, true cat
|-domestic cat, house cat, Felis domesticus, Felis catus
| |-kitty, kitty-cat, puss
| |-mouser
| |-alley cat
| |-tom, tomcat
| | \-gib
| |-Angora, Angora cat
| \-Siamese cat, Siamese
| \-blue point Siamese
\-wildcat
|-sand cat
|-European wildcat, catamountain, Felis silvestris
|-cougar, puma, catamount, mountain lion, painter, panther, Felis concolor
|-ocelot, panther cat, Felis pardalis
|-manul, Pallas's cat, Felis manul
\-lynx, catamount
|-common lynx, Lynx lynx
\-Canada lynx, Lynx canadensis
Run Code Online (Sandbox Code Playgroud)
如果您正在寻找与递归迭代器一起使用的更多HTML输出控件,请参阅以下问题,其中包含<ul><li>基于HTML输出的示例:
那么这一起看起来怎么样?作为github上的要点立即审查的代码.