如何用'for'循环或递归函数替换多个foreach循环?

时间:2009-08-18 17:30:07

标签: php recursion foreach

我遇到了一堆foreach循环,并且想知道是否有办法将它们简化为简单的'for'循环或递归函数?我正在尝试使用嵌套在彼此内部的元素生成HTML。我想我想要的是一个数组数组。但我不知道如何推进到目前为止我所创造的内容。 有人可以帮助我让这个怪物变得更加强大吗?谢谢!

这是我的代码:

$containers     = DISPLAY::displayParentElements($data);
$subcontainers  = DISPLAY::displayChildElements($data2);


foreach($containers as $parent) {
    $parentDiv = $parent['parentDiv'];
    echo '<div id="'.$parentDiv.'">';

    foreach($subcontainers as $child) {
        echo '<div id="'.$child['childDiv'].'">';

        foreach($subcontainers as $grandChild) {
            echo '<div id="'.$grandChild['grandChildDiv'].'">';

            foreach($subcontainers as $greatGrandChild) {
                echo '<div id="'.$greatGrandChild['greatGrandChildDiv'].'">';
                echo '</div>';
            }
            echo '</div>';
        }
        echo '</div>';
    }
echo '</div>';
}

结果将如下所示:

<div id="siteContainer">
  <div id="header">
        <div id="logoContainer">/div>
        <div id="logo"></div>
        <div id="links"></div>
        <div id="contactInfo">
              <div id="logoText">
                    <div id="shortDiv">
                          <div class="headerText"></div>
                    </div>
              </div>
         </div>
  </div>
  <div id="body">
        <div id="longDiv"></div>
        <div id="greetings"></div>
  </div>
<div>

$containers数组具有以下信息:

Array
(
    [attribute_value] => siteContainer
)

Array
(
    [attribute_value] => header
)

Array
(
    [attribute_value] => logoContainer
)

Array
(
    [attribute_value] => logo
)

Array
(
    [attribute_value] => logoText
)

Array
(
    [attribute_value] => links
)

Array
(
    [attribute_value] => contactInfo
)

Array
(
    [attribute_value] => body
)

Array
(
    [attribute_value] => longDiv
)

Array
(
    [attribute_value] => shortDiv
)

Array
(
    [attribute_value] => headerText
)

Array
(
    [attribute_value] => greetings
)

$subcontainers数组具有几乎相同的信息,但有一个额外的键:

Array
(
    [parent_container_name] => siteContainer
    [attribute_value] => header
)

Array
(
    [parent_container_name] => header
    [attribute_value] => logoContainer
)

Array
(
    [parent_container_name] => header
    [attribute_value] => logo
)

Array
(
    [parent_container_name] => contactInfo
    [attribute_value] => logoText
)

Array
(
    [parent_container_name] => header
    [attribute_value] => links
)

Array
(
    [parent_container_name] => header
    [attribute_value] => contactInfo
)

Array
(
    [parent_container_name] => siteContainer
    [attribute_value] => body
)

Array
(
    [parent_container_name] => body
    [attribute_value] => longDiv
)

Array
(
    [parent_container_name] => logoText
    [attribute_value] => shortDiv
)

Array
(
    [parent_container_name] => shortDiv
    [attribute_value] => headerText
)

Array
(
    [parent_container_name] => body
    [attribute_value] => greetings
)

我很确定这两个数组可以缩小为一个,或仅使用$containers数组。

5 个答案:

答案 0 :(得分:1)

我不能从你的帖子中说出来,但是你发布的代码是你拥有的代码,你发布的输出是你想要的输出吗?您发布的代码...我想说不会,但实际上我不知道php足够确定...不应该产生您发布的输出。事实上,如果它产生任何输出,我会感到震惊。您发布的数组具有键值对。没有任何键是'parentDiv','childDiv','grandChildDiv'或'greatGrandChildDiv'。相反,它们只是第一个'attribute_value',第二个'attribute_value'和第二个'parent_container_name'。

为了生成您想要的输出,请尝试以下几行:

$containerWasGenerated = new array();

echo '<div id="siteContainer">';
$containerWasGenerated['siteContainer'] = true;
foreach($containers as $container) {
    if($containerWasGenerated[$container['attribute_value'] != true) {
        generateSubcontainers($container, $containers, $subcontainers, $containerWasGenerated);
        $containerWasGenerated[$container['attribute_value']] = true;
    }
}
echo '</div>';

function generateSubcontainers($parent, $containers, $subcontainers, $containerWasGenerated) {
    echo '<div id="'.$parent['attribute_value'].'">';
    foreach($containers as $subcontainer) {
        if(getParent($subcontainer, $subcontainers) == $parent['attribute_value']) {
            generateContainer($subcontainer, $containers, $subcontainers, $containerWasGenerated);
            $containerWasGenerated[$subcontainer['attribute_value']] = true;
        }
    }
    echo '</div>';
}

function getParent($container, $subcontainers) {
    foreach($subconainters as $subcontainer) {
        if($subcontainer['attribute_value'] == $container['attribute_value']) {
            return $subcontainer['parent_container_name'];
        }
    }
}

免责声明:此代码未经测试,我的php有点生疏,所以它可能是多病的。但它应该是正确的方向。而且,它仍然非常低效。加速它的一种方法:保持子容器数组按parent_container_name排序,并编写一个更好的getParent()函数(例如使用二进制搜索)。希望这会有所帮助。

答案 1 :(得分:0)

这仅使用子容器和绝对父容器的名称。

function printChildren($parent, $children) {
    echo '<div id="'.$parent.'">'."\n";
        foreach($children as $child) {
            if($child[0] == $parent) {
                printChildren($child[1], $children);
            }
    }
    echo '</div>'."\n";
}

printChildren('siteContainer', $subcontainers);

注意:这不会很好地标记代码......

答案 2 :(得分:0)

您应该为$ container中的节点指定一个空值为parent_container_name的节点。这个 将代表顶级菜单节点。

接下来,使用array_merge将两个阵列粘合在一起。

现在您有一个具有公共节点结构的数组,您可以轻松向下递归,呈现与当前节点匹配的 parent_container_name 的子元素,并以递归方式重复。

我想你想要这样的东西:

$menuNodes; // The array of all nodes


function RenderMenu()
{
    // Grab all top-level nodes (nodes with no parent_container_name attribute)
    $topLevelNodes = array();

    foreach ($menuNodes as $node)
        if ($node["parent_container_name"] == null)
            $topLevelNodes[] = $node;


    foreach ($topLevelNodes as $node)
        RenderMenuNode($node);
}

function RenderMenuNode($node)
{
    print "<div id='" . $node["attribute_value"] ."'>";
    $node["isRendered"] = true;

    // This filtering callback will discard elements that are not children or have already been rendered.
    function Filter($element)
    {
        global $node;
        return (!$element["isRendered"] && $element["parent_container_name"] == $node["parent_container_name]"]);
    }

    $children = array_filter($menuNodes, 'Filter');

    foreach ($children as $child)
        RenderMenuNode($child);

    print "</div>";
}

我没有运行或测试过这段代码,它肯定会更有效率,但希望它能指出你正确的方向。理想情况下,您将从menuNodes数组中删除呈现的节点,这样您就不会不必要地重复迭代节点。

通过以这种方式递归地定义菜单结构,您可以根据需要嵌套菜单,而无需更改代码。希望这有帮助!

答案 3 :(得分:0)

执行此操作的最佳方法是使您的数组更像您想要表示的数据。因此,您将使键值对更加相关。我会创建以下数组:

$new_array = array(
    [siteContainer] => array(
        [header] => array(
            [0] => "logo",
            [1] => "links",
            [contactInfo"] => array (
                [0] => "logoInfo",

            ....

           )
        )
     )
 );

要在递归函数中执行以下操作,您需要编写:

function makeSubDiv($array)
{
    foreach($array as $key=>$value)
    {
         if(is_array($value))
         {
             echo '<div id="'.$key.'">';
                 makeSubDiv($value);
             echo '</div>';
         }
         else
         {
              echo '<div id="'.$value.'"></div>';
         }
    }
}

最后要合并你目前拥有的数组,我会有类似的东西。

// not sure why you have $containers because the data seems to be in subcontainers
$containers     = DISPLAY::displayParentElements($data);
$subcontainers  = DISPLAY::displayChildElements($data2);

function mergeToNested($subcontainers)
{
    $return = array();
    foreach($containers as $values)
    {
        $key = $values["parent_container_name";
        if(isset($return[$key]))
        {
            if(is_array($return[$key]))
            {
                array_merge($return[$key], mergeToNested($return[$key]));
            }
            else
            {
                $return[$key] = array($values["attribute_value"]);
            }
        }
        else if($index = array_search($key, $return) !== false)
        {
             unset($return[$index]);
             $return[$key] = array();
        }
        else
        {
             $return[] = $key;
        }
     }

     return $return;
}

Argh,希望您可以嵌套$ container和$ subcontainers,而无需通过mergeToNested()函数推送它们。

注意

此代码(尤其是MergeToNested函数)未经过测试,它是如何获取所需数据的想法,它可能无法按预期合并到所有嵌套级别。 ...随时测试和编辑任何更改。

答案 4 :(得分:0)

我对我的回答做了很多改动,我认为最好把它换成新的而不是编辑。

这次我包括我正在使用的输入数组,以防我错误解释你的开头。我还创建了一个函数来查找所有绝对父母,即没有父母的父母。

// list of subcontainers and their parents
$subcontainers = array(
array('siteContainer', 'header'), array('header', 'logoContainer'),
array('header', 'logo'), array('contactInfo', 'logoText'),
array('header', 'links'), array('header', 'contactInfo'),
array('siteContainer', 'body'), array('body', 'longDiv'),
array('logoText', 'shortDiv'), array('shortDiv', 'headerText'),
array('body', 'greetings'));

// recursively prints each container
function printChildren($parent, $children) {
    echo '<div id="'.$parent.'">'."\n";
    foreach($children as $child) {
        if($child[0] == $parent) {
            printChildren($child[1], $children);
        }
    }
    echo '</div>'."\n";
}

// finds all parents that have no parents
function findAbsParent($subcontainers) {
    $absParents = array();
    foreach($subcontainers as $parent) {
        $isAbs = true;
        foreach($subcontainers as $child) {
            if($parent[0] == $child[1]) {
                $isAbs = false;
            }
        }
        if($isAbs) {$absParents[] = $parent[0];}
    }
    return array_unique($absParents);
}

$absparents = findAbsParent($subcontainers);
// this is only needed if it's possible to have more then one absolute parent
if(is_array($absparents)) {
    foreach($absparents as $absparent) {printChildren($absparent, $subcontainers);}
} else {
    printChildren($absparents, $subcontainers);
}