Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

For loop, wrap every two posts in a div

Tags:

jekyll

liquid

Basically, I am using Jekyll (which uses the Liquid templating language) and I am trying to write a for loop which wraps every two items in a div.

This is my current loop:

<div>
  {% for post in site.posts %}
    <a href="{{ post.url }}">{{ post.title }}</a>
  {% endfor %}
</div>

Which would output four posts like so:

<div>
  <a href="#">Title</a>
  <a href="#">Title</a>
  <a href="#">Title</a>
  <a href="#">Title</a>
</div>

My desired output for four posts is:

<div>
  <a href="#">Title</a>
  <a href="#">Title</a>
</div>

<div>
  <a href="#">Title</a>
  <a href="#">Title</a>
</div>

How can I accomplish this?

like image 438
Tom Avatar asked Jan 04 '14 17:01

Tom


3 Answers

If the number of <div>s and posts is fixed (which seems to be the case based on which answer you selected), there's a shorter way to get the same output - using limit and offset:
(Liquid's approach to paging)

<div>
  {% for post in site.posts limit: 2 %}
    <a href="{{ post.url }}">{{ post.title }}</a>
  {% endfor %}
</div>
<div>
  {% for post in site.posts limit: 2 offset: 2 %}
    <a href="{{ post.url }}">{{ post.title }}</a>
  {% endfor %}
</div>

Even better solution:

If the number of posts is not fixed (so when you have 100 posts, you want 50 <div>s with two posts each), then you can use forloop.index (which was already mentioned in most of the other answers), and use modulo to find out if the current index is even or odd:

{% for post in site.posts %}
  {% assign loopindex = forloop.index | modulo: 2 %}
  {% if loopindex == 1 %}
    <div>
      <a href="{{ post.url }}">{{ post.title }}</a>
  {% else %}
      <a href="{{ post.url }}">{{ post.title }}</a>
    </div>
  {% endif %}
{% endfor %}

This returns your desired output as well, but works for any number of posts.

like image 58
Christian Specht Avatar answered Nov 02 '22 16:11

Christian Specht


I know I'm late to the game but I found what I feel is a fairly elegant solution that doesn't feel hacky.

With for loop's limit and offset params, we can iterate one row at a time, N posts per row.

First, we count the number of rows we'll need to enumerate over:

{% assign rows = site.posts.size | divided_by: 2.0 | ceil %}

The Ruby equivalent would be rows = (posts.size / 2.0).ceil (odd numbers get their own row).

Next, we'll iterate over the rows:

{% for i in (1..rows) %}
  <div>

Now we need to calculate the collection offset with (i - 1) * 2 (using forloop.index0):

  {% assign offset = forloop.index0 | times: 2 %}

Then we can iterate over the slice of posts starting at the row's offset (equivalent to posts[offset, 2] in Ruby):

    {% for post in site.posts limit:2 offset:offset %}
      <a href="{{ post.url }}">{{ post.title }}</a>
    {% endfor %}

Close the row div element and for loop:

  </div>
{% endfor %}

That's it!

In Ruby, this would be:

rows = (posts.size / 2.0).ceil # the number of rows
(1..rows).each do |i|
  offset = (i - 1) * 2
  # <div>
  posts[offset, 2].each do |post|
    # <a href="#{post.url}>#{post.title}</a>
  end
  # </div>
end

All together now, in Liquid:

{% assign rows = site.posts.size | divided_by: 2.0 | ceil %}
{% for i in (1..rows) %}
  {% assign offset = forloop.index0 | times: 2 %}
  <div>
    {% for post in site.posts limit:2 offset:offset %}
      <a href="{{ post.url }}">{{ post.title }}</a>
    {% endfor %}
  </div>
{% endfor %}

Hope this helps someone!

like image 34
Matt Todd Avatar answered Nov 02 '22 15:11

Matt Todd


I've just come across this (https://gist.github.com/leemachin/2366832) which is a much better solution then ones posted in other answers, bare in mind you'll need this plugin (https://gist.github.com/leemachin/2366832#file-modulo-filter-rb) for it to work:

{% for post in paginator.posts %}

  {% capture modulo %}{{ forloop.index0 | mod:2 }}{% endcapture %}

  {% if modulo == '0' or forloop.first %}
    <div>
  {% endif %}

    <a href="{{ post.url }}">{{ post.title }}</a>

  {% if modulo == '1' or forloop.last %}
    </div>
  {% endif %}

{% endfor %}
like image 6
Tom Avatar answered Nov 02 '22 15:11

Tom