Отфильтровать или сгруппировать коллекцию в Liquid

Для простого личного блога Jekyll я хочу сгруппировать site.posts по атрибуту post, lang (язык). Это либо "en", "nl", либо nil.

Затем я хочу отобразить два списка сообщений. В настоящее время у меня есть:

<section lang="nl">
<h2>Nederlandse Artikelen</h2>
<ul class="posts">
  {% for post in site.posts limit:50 %}
    {% if post.lang == "nl" %}
      {% include li_for_post_with_date.yml %}
    {% endif %}
  {% endfor %}
</ul>
<a href="archief.html">Archief »</a>
</section>
<section lang="en">
<h2>English Articles</h2>
<ul class="posts">
  {% for post in site.posts limit:50 %}
    {% if post.lang == nil or post.lang == "en" %}
      <li><span>{{ post.date | date_to_string }}</span> &raquo; <a href="{{ BASE_PATH }}{{ post.url }}">{{ post.title }}</a></li>
    {% endif %}
  {% endfor %}
</ul>

Это имеет две проблемы:

  1. Самый раздражающий; когда в последних 50 постах 47 lang=en и 3 lang=nl, теперь я получаю перекошенный список. Мне нужно 25 записей lang=en и 25 записей lang=nl.
  2. Петля проходится дважды, это кажется мне неэффективным.

Есть ли способ назначить или подготовить коллекцию в Liquid? Таким образом, я мог один раз перебрать site.posts и подготовить вложенную коллекцию, например site.grouped_posts[en].

Или еще одна хитрость?

Решение

Как указывает Том Кларксон, поддержание счетчика — правильное направление. Однако увеличение счетчика появилось только в последних версиях Liquid, версия 2.2.2, работающая на Github (где компилируется моя жидкость), без возможности увеличения счетчика. Само решение Тома тоже не работает, потому что Liquid превращает переменную counter в строку, которую нельзя сравнивать с <.

Я создал хак, добавив строку и подсчитав символы.

{% assign counter = '.' %}
{% for post in site.posts %}
  {% if counter.size <= 25 and post.lang == "nl" %}
    {% capture counter %}{{ counter | append:'.' }}{% endcapture %}
    {% include li_for_post_with_date.yml %}
  {% endif %}
{% endfor %}

Как говорится, некрасиво, поэтому, если есть более чистые решения, добавьте решение!


person berkes    schedule 26.11.2012    source источник


Ответы (2)


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

{% for post in site.posts %}
    {% if counter < 25 and post.lang == nil or post.lang == "en" %}
        {% capture counter %}{{ counter | plus:1 }}{% endcapture %} 
        <li></li>
    {% endif %}
{% endfor %}

Код не тестировался, но что-то похожее должно работать.

person Tom Clarkson    schedule 27.11.2012
comment
Интересное направление. Capture же превращает counter в строку, с 25 после первой итерации не сравнить :/. В более поздних жидких версиях есть инкремент, но не в версии, работающей на Github. - person berkes; 27.11.2012
comment
Попробуйте некоторые варианты - сравнение со строкой 25 может сработать, или если плюс не будет добавляться к строке, вы можете использовать длину строки, а не ее значение в качестве счетчика. - person Tom Clarkson; 28.11.2012
comment
Да, я рискну в длину строки. Где я просто соединяю ряд точек и сравниваю это :). - person berkes; 28.11.2012
comment
@berkes Конечно, {% increment somevar %}, в отличие от любого другого выражения {% %} Liquid, является оператором вывода, поэтому вам все равно придется его захватить (и преобразовать в строку). Liquid — это действительно грубый, плохо продуманный хак системы шаблонов. Даже создание простого эффективного целочисленного счетчика циклов потребует часов времени. - person ; 10.01.2015

К сожалению, фильтры Liquid не работают в for циклах. Однако они работают в присваиваниях переменных.

{% assign posts_by_lang = site.posts | group_by: "lang" %}
{% for lang in posts_by_lang %}
  {{ lang.name }}
  {% for post in lang.items limit: 25 %}
    {{ post.title }}
  {% endfor %}
{% endfor %}

Атрибуты name и items генерируются group_by, остальное как обычно.

person Joel Hamme    schedule 26.01.2015