代码之家  ›  专栏  ›  技术社区  ›  Topher Fangio

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

  •  1
  • Topher Fangio  · 技术社区  · 15 年前

    这是我以前遇到的一个问题,我还没有找到一个优雅的解决方案,所以我想我会请求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 回复  |  直到 15 年前
        1
  •  1
  •   Ants Aasma    15 年前

    用相似的值对项目进行分组很难称为算法。它更多的是一种编码模式,如果还有其他的话。

    编码的一个好方法是将机制与意图分离。在这种情况下,机制是如何跟踪键值以查找分组边界,其目的是为每个顺序组输出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以返回实现迭代器接口的对象,并且只从输入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
  •  1
  •   Rafał Dowgird    15 年前

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

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

        3
  •  0
  •   mdm Mr. Sajid Shaikh    15 年前

    我已经有一段时间没做过任何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。

    推荐文章