class Liquid::For

“For” iterates over an array or collection. Several useful variables are available to you within the loop.

Basic usage:

{% for item in collection %}
  {{ forloop.index }}: {{ item.name }}
{% endfor %}

Advanced usage:

{% for item in collection %}
  <div {% if forloop.first %}class="first"{% endif %}>
    Item {{ forloop.index }}: {{ item.name }}
  </div>
{% else %}
  There is nothing in the collection.
{% endfor %}

You can also define a limit and offset much like SQL. Remember that offset starts at 0 for the first item.

  {% for item in collection limit:5 offset:10 %}
    {{ item.name }}
  {% end %}

To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)

Available variables:

forloop.name

'item-collection'

forloop.length

Length of the loop

forloop.index

The current item's position in the collection; forloop.index starts at 1. This is helpful for non-programmers who start believe the first item in an array is 1, not 0.

forloop.index0

The current item's position in the collection where the first item is 0

forloop.rindex

Number of items remaining in the loop (length - index) where 1 is the last item.

forloop.rindex0

Number of items remaining in the loop where 0 is the last item.

forloop.first

Returns true if the item is the first item.

forloop.last

Returns true if the item is the last item.

forloop.parentloop

Provides access to the parent loop, if present.

Constants

Syntax

Attributes

collection_name[R]
from[R]
limit[R]
variable_name[R]

Public Class Methods

new(tag_name, markup, options) click to toggle source
Calls superclass method Liquid::Block::new
# File lib/liquid/tags/for.rb, line 53
def initialize(tag_name, markup, options)
  super
  @from = @limit = nil
  parse_with_selected_parser(markup)
  @for_block = new_body
  @else_block = nil
end

Public Instance Methods

nodelist() click to toggle source
# File lib/liquid/tags/for.rb, line 73
def nodelist
  @else_block ? [@for_block, @else_block] : [@for_block]
end
parse(tokens) click to toggle source
# File lib/liquid/tags/for.rb, line 61
def parse(tokens)
  if parse_body(@for_block, tokens)
    parse_body(@else_block, tokens)
  end
  if blank?
    @else_block&.remove_blank_strings
    @for_block.remove_blank_strings
  end
  @else_block&.freeze
  @for_block.freeze
end
render_to_output_buffer(context, output) click to toggle source
# File lib/liquid/tags/for.rb, line 82
def render_to_output_buffer(context, output)
  segment = collection_segment(context)

  if segment.empty?
    render_else(context, output)
  else
    render_segment(context, output, segment)
  end

  output
end
unknown_tag(tag, markup, tokens) click to toggle source
Calls superclass method Liquid::Block#unknown_tag
# File lib/liquid/tags/for.rb, line 77
def unknown_tag(tag, markup, tokens)
  return super unless tag == 'else'
  @else_block = new_body
end

Protected Instance Methods

lax_parse(markup) click to toggle source
# File lib/liquid/tags/for.rb, line 96
def lax_parse(markup)
  if markup =~ Syntax
    @variable_name   = Regexp.last_match(1)
    collection_name  = Regexp.last_match(2)
    @reversed        = !!Regexp.last_match(3)
    @name            = "#{@variable_name}-#{collection_name}"
    @collection_name = parse_expression(collection_name)
    markup.scan(TagAttributes) do |key, value|
      set_attribute(key, value)
    end
  else
    raise SyntaxError, options[:locale].t("errors.syntax.for")
  end
end
strict_parse(markup) click to toggle source
# File lib/liquid/tags/for.rb, line 111
def strict_parse(markup)
  p = Parser.new(markup)
  @variable_name = p.consume(:id)
  raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')

  collection_name  = p.expression
  @collection_name = parse_expression(collection_name)

  @name     = "#{@variable_name}-#{collection_name}"
  @reversed = p.id?('reversed')

  while p.look(:id) && p.look(:colon, 1)
    unless (attribute = p.id?('limit') || p.id?('offset'))
      raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
    end
    p.consume
    set_attribute(attribute, p.expression)
  end
  p.consume(:end_of_string)
end

Private Instance Methods

collection_segment(context) click to toggle source
# File lib/liquid/tags/for.rb, line 134
def collection_segment(context)
  offsets = context.registers[:for] ||= {}

  from = if @from == :continue
    offsets[@name].to_i
  else
    from_value = context.evaluate(@from)
    if from_value.nil?
      0
    else
      Utils.to_integer(from_value)
    end
  end

  collection = context.evaluate(@collection_name)
  collection = collection.to_a if collection.is_a?(Range)

  limit_value = context.evaluate(@limit)
  to = if limit_value.nil?
    nil
  else
    Utils.to_integer(limit_value) + from
  end

  segment = Utils.slice_collection(collection, from, to)
  segment.reverse! if @reversed

  offsets[@name] = from + segment.length

  segment
end
render_else(context, output) click to toggle source
# File lib/liquid/tags/for.rb, line 211
def render_else(context, output)
  if @else_block
    @else_block.render_to_output_buffer(context, output)
  else
    output
  end
end
render_segment(context, output, segment) click to toggle source
# File lib/liquid/tags/for.rb, line 166
def render_segment(context, output, segment)
  for_stack = context.registers[:for_stack] ||= []
  length    = segment.length

  context.stack do
    loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])

    for_stack.push(loop_vars)

    begin
      context['forloop'] = loop_vars

      segment.each do |item|
        context[@variable_name] = item
        @for_block.render_to_output_buffer(context, output)
        loop_vars.send(:increment!)

        # Handle any interrupts if they exist.
        next unless context.interrupt?
        interrupt = context.pop_interrupt
        break if interrupt.is_a?(BreakInterrupt)
        next if interrupt.is_a?(ContinueInterrupt)
      end
    ensure
      for_stack.pop
    end
  end

  output
end
set_attribute(key, expr) click to toggle source
# File lib/liquid/tags/for.rb, line 197
def set_attribute(key, expr)
  case key
  when 'offset'
    @from = if expr == 'continue'
      Usage.increment('for_offset_continue')
      :continue
    else
      parse_expression(expr)
    end
  when 'limit'
    @limit = parse_expression(expr)
  end
end