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

时间:2010-01-11 20:28:25

标签: algorithm language-agnostic design-patterns

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

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

非常感谢!

表格(为了清晰起见,添加间距

Choice ID     Choice Body     Question ID     Question Body
---------------------------------------------------------------------
1             Yes, very much  1               Do you like sandwiches?
2             Somewhat        1               Do you like sandwiches?
3             Not at all      1               Do you like sandwiches?
4             I hate them     1               Do you like sandwiches?

5             Sure, why not   2               Do you like apples?
6             Yesh, I guess   2               Do you like apples?
7             What are those  2               Do you like apples?

8             Yes, very much  3               Do you like chips?
9             Not at all      3               Do you like chips?

期望输出:

<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']

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

按类似值对项目进行分组很难称为算法。如果不是其他的话,它更像是一种编码模式。

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

Python for instance有一个名为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接口的对象,并且只从输入iterable中延迟提取。

$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 :(得分:0)

因为我已经完成了任何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。