从线性数组创建一个 PHP 的多维数组

Create a multi-dimensional array with PHP from lineal array

TL;DR 我正在寻找一个函数来从 PHP 中的一维数组创建嵌套 <ol> 列表。

1) 目前我的测试页中有这个简化的标记:

<h2>Spiders</h2>
<h2>Beetles</h2>
<h3>External morphology</h3>
<h4>Head</h4>
<h4>Thorax</h4>
<h4>Legs</h4>
<h3>Anatomy and physiology</h3>
<h2>Ants</h2>

2) 然后通过一个非常简单的函数将其捕获到一个一维数组中,如下所示:

array
(
    0 => "H2 Spiders",
    1 => "H2 Beetles",
    2 => "H3 External morphology",
    3 => "H4 Head",
    4 => "H4 Thorax",
    5 => "H4 Legs",
    6 => "H3 Anatomy and physiology"
    7 => "H2 Ants"
);

3) 这是棘手的部分,因为我使用带有这些过于复杂的 if 语句的 next 循环来填充多维数组。

$toc = array ();
//
foreach ($array as $value) {
    $value_arr = explode(' ', $value, 2);
    $depth = str_replace("H", "", $value_arr[0]);
    $content = $value_arr[1];
    //
    if ($depth == 1) $toc[$title] = null;
    elseif ($depth == 2) {
        if (empty (end ($toc))) $toc[array_key_last ($toc)] = array ($title => null);
        else $toc[array_key_last ($toc)][$title] = null;
    } elseif ($depth == 3) {
        if (empty (end ($toc[array_key_last ($toc)]))) $toc[array_key_last ($toc)][array_key_last ($toc[array_key_last ($toc)])] = array ($title => null);
        else $toc[array_key_last ($toc)][array_key_last ($toc[array_key_last ($toc)])][$title] = '';
    }
}

输出:

Array (
[Spiders] => 
[Beetles] => Array
    (
        [External morphology] => Array
            (
                [Head] => 
                [Thorax] => 
                [Legs] => 
            )
        [Anatomy and physiology] => 
    )
[Ants] => 
)

4) 最后用这个函数解析成一个完美缩进的 html 列表。

function table_of_contents ($toc, $output = '') {
    foreach ($toc as $key => $value) {
        $output = "$output <li><a href='#@" . sanitize_title ($key) . "'>$key</a>" . (is_array ($value) ? table_of_contents ($value) : null) . '</li>';
    }
    //
    return "<ol>$output</ol>";
}
//
table_of_contents ($toc);

-

  1. 蜘蛛
  2. 甲虫
    1. 外部形态
      1. 头部
      2. 胸部
      3. 腿部
    2. 解剖学和生理学
  3. 蚂蚁

第一步、第二步和第四步一切正常,但我目前的方法有一个缺点,即在第三步中只允许我从第一个数组开始最多三个级别的深度。

我的问题是是否有更有效、更简洁的方法来创建具有(可能)递归函数或类似函数的多维数组?

使用 preg_match_all 可能更容易解析您的输入;这可以为您提供深度数组和相关值。然后你可以遍历这些数组,当深度增加时打开一个 <ol>,当深度减小时关闭它:

$html = '<h2>Spiders</h2>
<h2>Beetles</h2>
<h3>External morphology</h3>
<h4>Head</h4>
<h4>Thorax</h4>
<h4>Legs</h4>
<h3>Anatomy and physiology</h3>
<h2>Ants</h2>';

preg_match_all('/<h(\d)>([^<]+)/', $html, $matches);
$cdepth = $matches[1][0] - 1;
foreach ($matches[1] as $key => $depth) {
    if ($depth > $cdepth) {
        echo "\n" . str_repeat(' ', $cdepth * 4) . "<ol>\n";
    }
    elseif ($depth < $cdepth) {
        echo "</li>\n" . str_repeat(' ', $depth * 4) . "</ol>\n" . str_repeat(' ', $depth * 4) . "</li>\n";
    }
    else {
        echo "</li>\n";
    }
    $cdepth = $depth;
    echo str_repeat(' ', $cdepth * 4) . "<li><a href=\"{$matches[2][$key]}\">{$matches[2][$key]}</a>";
}
while ($cdepth-- >= $matches[1][0]) {
    echo "</li>\n" . str_repeat(' ', $cdepth * 4) . "</ol>\n";
}

输出:

<ol>
    <li><a href="Spiders">Spiders</a></li>
    <li><a href="Beetles">Beetles</a>
    <ol>
        <li><a href="External morphology">External morphology</a>
        <ol>
            <li><a href="Head">Head</a></li>
            <li><a href="Thorax">Thorax</a></li>
            <li><a href="Legs">Legs</a></li>
        </ol>
        </li>
        <li><a href="Anatomy and physiology">Anatomy and physiology</a></li>
    </ol>
    </li>
    <li><a href="Ants">Ants</a></li>
</ol>

Demo on 3v4l.org

更新

如果出于输出以外的其他原因需要第三阶段数组,可以使用此递归函数生成它(它仍然适用于 preg_match_all 的输出):

$html = '<h2>Spiders</h2>
<h2>Beetles</h2>
<h3>External morphology</h3>
<h4>Head</h4>
<h4>Thorax</h4>
<h4>Legs</h4>
<h5>Feet</h5>
<h3>Anatomy and physiology</h3>
<h2>Ants</h2>';


function push_values(&$k, $depth, $content) {
    $output = array();
    $cdepth = $depth[$k];
    while ($k < count($depth)) {
        if ($depth[$k] == $cdepth) {
            $output[$content[$k]] = '';
            $k++;
        }
        elseif ($depth[$k] > $cdepth) {
            $output[$content[$k-1]] = push_values($k, $depth, $content);
        }
        else {
            return $output;
        }
    }
    return $output;
}

preg_match_all('/<h(\d)>([^<]+)/', $html, $matches);
$key = 0;
print_r(push_values($key, $matches[1], $matches[2]));

输出:

Array
(
    [Spiders] => 
    [Beetles] => Array
        (
            [External morphology] => Array
                (
                    [Head] => 
                    [Thorax] => 
                    [Legs] => Array
                        (
                            [Feet] => 
                        )    
                )    
            [Anatomy and physiology] => 
        )    
    [Ants] => 
)

Demo on 3v4l.org

最后,我建议对@Nick 发布的第一个代码进行小的修复,该代码会在下一个场景中导致不希望的 HTML 输出,其中深度差异大于 2 将改变最表面的兄弟姐妹;例如,<h6> 之后的 <h3> 变为 <h5> 并且下一个 <h2> 被解析为 <h4>:

$html = '<h2>Spiders</h2>
<h2>Beetles</h2>
<h3>External morphology</h3>
<h4>Head</h4>
<h4>Thorax</h4>
<h4>Legs</h4>
<h5>Feet</h5>
<h6>Toes</h6>
<h3>Anatomy and physiology</h3>
<h2>Ants</h2>';

foreach ($matches[1] as $key => $depth) {
    if ($depth > $cdepth) {
        echo "\n" . str_repeat(' ', $cdepth * 4) . "<ol>\n";
    }
    elseif ($depth < $cdepth) {
        echo "</li>\n" . str_repeat(' ', $depth * 4) . "</ol>\n" . str_repeat(' ', $depth * 4) . "</li>\n";
    }
    else {
        echo "</li>\n";
    }
    $cdepth = $depth;
    echo str_repeat(' ', $cdepth * 4) . "<li><a href=\"{$matches[2][$key]}\">{$matches[2][$key]}</a>";
}
while ($cdepth-- >= $matches[1][0]) {
    echo "</li>\n" . str_repeat(' ', $cdepth * 4) . "</ol>\n";
}

输出:

<ol>
    <li><a href="Spiders">Spiders</a></li>
    <li><a href="Beetles">Beetles</a>
    <ol>
        <li><a href="External morphology">External morphology</a>
        <ol>
            <li><a href="Head">Head</a></li>
            <li><a href="Thorax">Thorax</a></li>
            <li><a href="Legs">Legs</a>
            <ol>
                <li><a href="Feet">Feet</a>
                <ol>
                    <li><a href="Toes">Toes</a></li>
        </ol>
        </li>
        <li><a href="Anatomy and physiology">Anatomy and physiology</a></li>
    </ol>
    </li>
    <li><a href="Ants">Ants</a></li>
</ol>
</li></ol></li></ol>

-

为了解决这个问题,我刚刚添加了另一个 while 语句以在添加下一个 <li> 元素之前放置正确数量的 </li></ol>,现在可以正确验证 W3C 检查器。

foreach ($matches[1] as $key => $depth) {
    if ($depth > $cdepth) {
        echo "\n" . str_repeat(' ', $cdepth * 4) . "<ol>\n";
    }
    elseif ($depth < $cdepth) {
        while ($cdepth -- > $depth) {
            echo "</li>\n" . str_repeat(' ', $depth * 4) . "</ol>\n" . str_repeat(' ', $depth * 4) . "\n";
        }
    }
    else {
        echo "</li>\n";
    }
    $cdepth = $depth;
    echo str_repeat(' ', $cdepth * 4) . "<li><a href=\"{$matches[2][$key]}\">{$matches[2][$key]}</a>";
}
while ($cdepth-- >= $matches[1][0]) {
    echo "</li>\n" . str_repeat(' ', $cdepth * 4) . "</ol>\n";
}

输出:

<ol>
    <li><a href="Spiders">Spiders</a></li>
    <li><a href="Beetles">Beetles</a>
    <ol>
        <li><a href="External morphology">External morphology</a>
        <ol>
            <li><a href="Head">Head</a></li>
            <li><a href="Thorax">Thorax</a></li>
            <li><a href="Legs">Legs</a>
            <ol>
                <li><a href="Feet">Feet</a>
                <ol>
                    <li><a href="Toes">Toes</a></li>
        </ol>
        
</li>
        </ol>
        
</li>
        </ol>
        
        <li><a href="Anatomy and physiology">Anatomy and physiology</a></li>
    </ol>
    
    <li><a href="Ants">Ants</a></li>
</ol>