用于迭代循环和打印开始和结束标记的标准模式

Posted

技术标签:

【中文标题】用于迭代循环和打印开始和结束标记的标准模式【英文标题】:Standard Pattern for Iterating Over Loop and Printing Start and End Tags 【发布时间】:2010-01-11 20:28:25 【问题描述】:

这是我之前遇到过的一个问题,我还没有找到一个优雅的解决方案,所以我想我会寻求 SO 的帮助。

我正在遍历一个数组并打印该数组中的一些信息,并且无法弄清楚如何打印我的开始和结束 <div> 标签。下面是一个示例表和所需的输出以及我当前算法的基本实现。我希望有人可以指出我打印数据的更好算法。我是用 php 做的,但是一个通用的算法会很棒。

非常感谢!

表格:为清楚起见添加了间距

选择 ID 选择正文 问题 ID 问题正文 -------------------------------------------------- ------------------ 1 是的,非常喜欢 1 你喜欢三明治吗? 2 有点 1 你喜欢三明治吗? 3 一点也不 1 你喜欢三明治吗? 4 我讨厌他们 1 你喜欢三明治吗? 5 当然,为什么不呢 2 你喜欢苹果吗? 6 是的,我猜 2 你喜欢苹果吗? 7 那是什么 2 你喜欢苹果吗? 8 是的,非常喜欢 3 你喜欢薯片吗? 9 一点也不 3 你喜欢薯片吗?

所需的输出:

<div class='question' id='1'>
  <p>Do you like sandwiches?</p>

  <div class='choices'>
    <span class='choice'>Yes, very much</span>
    <span class='choice'>Somewhat</span>
    <span class='choice'>Not at all</span>
    <span class='choice'>I hate them</span>
  </div>
</div>

<div class='question' id='2'>
  <p>Do you like apples?</p>

  <div class='choices'>
    <span class='choice'>Sure, why not</span>
    <span class='choice'>Yeah, I guess</span>
    <span class='choice'>What are those</span>
  </div>
</div>

<div class='question' id='3'>
  <p>Do you like chips?</p>

  <div class='choices'>
    <span class='choice'>Yes, very much</span>
    <span class='choice'>Not at all</span>
  </div>
</div>

我目前使用的基本算法:

$last_id = null;
while ($choice = pg_fetch_array($choices)) 
    if ($last_id != $choice['id']) 
        if ($last_id != null) 
          echo "</div>";
        

        echo "<div id='$choice[id]'>";
    

    // Print choice info

    $last_id = $choice['id'];


if ($last_id != null) 
    echo "</div>";

注意:我使用这种方式的原因是为了优化。这只需要一个数据库查询,就会有很多结果。我不想为每个问题做一个查询来获得它的选择。我知道怎么做,而且更容易,但不是很高效或快速。

编辑 1: 修正代码,算法现在可以工作,但仍然不漂亮。对于评论者:pg_fetch_array() 是一个 PostgreSQL 函数,它基本上创建了一个关联数组。非常类似于一个物体。允许您只要求$choice['id']$choice['body']

【问题讨论】:

嗨,你能解释一下 pg_fetch_array 吗?很久以前我用过 PHP,所以我不太了解上下文。 在文章末尾添加了一些关于它的信息 :-) 您需要在循环后关闭&lt;/div&gt; - 请参阅我的答案。 【参考方案1】:

按相似值对项目进行分组很难称为算法。这更像是一种编码模式。

编码的一种好方法是将机制与意图分开。在这种情况下,机制是如何跟踪键值以找到分组边界,目的是为每个顺序组输出 html

例如,Python 有一个名为 groupby 的库函数来执行此操作。因此,在 Python 中,代码看起来像这样(忽略为此使用模板库的事实):

from itertools import groupby

def question_from_row(row):
    return dict(id=row['question_id'], body=row['question_body'])

for question, choices in groupby(questions, key=question_from_row):
    print('<div class="question" id="%s">' % question['id'])
    print('  <p>%s</p>\n' % question['body'])
    print('  <div class="choices">')
    for choice in choices:
        print('<span class="choice">%s</span>' % choice['choice_body'])
    print('  </div>')
    print('</div>')

据我所知,PHP 没有内置任何类似的东西,但简单的实现非常简单:

function array_groupby($input, $keyfunc) 
    $output = array();
    $last_key = null;
    $current = null;
    foreach ($input as $item) 
        $item_key = $keyfunc($item);
        if ($last_key === null || $item_key != $last_key) 
            if ($current !== null) 
                $output[] = $current;
            
            $last_key = $item_key;
            $current = array();
        
        $current[] = $item;
    
    if ($current !== null) 
        $output[] = $current;
    
    return $output;

这将是您包含的典型库代码。处理输出的代码变得相当琐碎。与分组的完成方式完全隔离。例如,您可以更改 array_groupby 以返回一个实现 Iterator 接口的对象,并且只从输入可迭代对象中延迟获取。

$questions = array_groupby(pg_fetch_array($choices),
    function($choice)  return $choice['id']; );

foreach ($questions as $choices) 
    $question = $choices[0];
    echo '<div class="question" id="'.$question['id'].'">';
    echo '<p>'.$question['body'].'</p>';
    echo '<div class="choices">';
    foreach ($choices as $choice) 
        echo '<span class="choice">'.$choice['choice_body'].'</span>';
    
    echo '</div>';
    echo '</div>';

这个例子使用了 PHP 5.3 的闭包特性。在旧版本中,指定提取分组键的函数会稍微难看,可能需要面向对象的方法。

【讨论】:

感谢蚂蚁!你是绝对正确的。它更像是一种模式,而不是一种算法。我曾考虑过分组,但只是非常简短。这很好地巩固了我的想法。感谢您提供如此深思熟虑和陈述明确的答案!【参考方案2】:

如果新读取的 id 与最后一个不同(并且最后一个不为空),则应在循环开始时打印结束 &lt;/div&gt;

此外,在循环之后(同样,如果最后一个 id 不为空,这意味着根本没有组)您需要关闭最后一个组。

【讨论】:

谢谢!我实际上是这样做的,只是在打印之前我没有检查 $last_id 是否为空。我会更新代码。您知道对此有更好的解决方案吗? 并非如此 - 我相信这是执行此操作的标准方法。您可以通过将内容放入 groupstart()groupend() 之类的函数中并在循环中调用这些函数来减少代码重复(打印 div 两次)。【参考方案3】:

自从我做任何 PHP 以来已经有一段时间了,但我会尝试这样的事情......

// print leading div for very first in array
echo "<div>";

$last_question = null;
while ($choice = pg_fetch_array($choices)) 

    // print choice info
    echo "<span ...>";

    if($last_question != $choice['question_id'])
    
        // print trailing div for last one
        echo "</div>";

        // print leading div for next one
        echo "<div>";
    


    // set last question
    $last_question = $choice['question_id'];



// print trailing div for very last in array
echo "</div>";

可能需要改进以确保它不会在最后打印额外的 div。

【讨论】:

这并不比我现有的解决方案好多少。更不用说,第一个 &lt;div&gt; 打印必须在循环内,以便我可以将问题的 id 作为 div 的一部分打印。

以上是关于用于迭代循环和打印开始和结束标记的标准模式的主要内容,如果未能解决你的问题,请参考以下文章

循环结束后从头开始重新开始迭代循环 - JS

正则表达式匹配

正则表达式用于匹配 img 标记的开始和结束尖括号内的所有文本

XML详解

JSTL标签之c:foreach,c:if标签小结

flask jinja2内置变量