Стандартный шаблон для перебора циклов и печати начальных и конечных тегов - PullRequest
1 голос
/ 11 января 2010

Это проблема, с которой я сталкивался раньше, и мне еще предстоит найти элегантное решение, поэтому я подумал, что буду просить о помощи 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 ]

1 голос
/ 12 января 2010

Группирование элементов по сходным значениям вряд ли можно назвать алгоритмом. Это скорее шаблон кодирования, если что-нибудь еще.

Хороший способ написания кода - отделить механизм от намерения. В этом случае механизм состоит в том, как отслеживать значения ключей, чтобы найти границы группировки, и цель состоит в том, чтобы вывести 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. В более старых версиях указание функции для извлечения группирующего ключа было бы несколько уродливее, возможно, требуя объектно-ориентированного подхода.

1 голос
/ 11 января 2010

Вы должны напечатать закрывающий </div> в начале цикла, если только что прочитанный идентификатор отличается от последнего (и последний не равен нулю).

Кроме того, после цикла (опять же, если последний идентификатор не равен нулю, что будет означать, что групп вообще не было), вам необходимо закрыть последнюю группу.

0 голосов
/ 11 января 2010

Прошло много времени с тех пор, как я написал 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.

...