fluid icon indicating copy to clipboard operation
fluid copied to clipboard

forloop.first ambiguity with offset

Open sebastienros opened this issue 3 years ago • 5 comments

If offset is defined, what should first represent? The first item of the collection or the first iteration element? Same with offset: continue.

sebastienros avatar Jan 18 '22 20:01 sebastienros

Hi,

I've found forloop.first and forloop.last to be the first and last iteration of the loop, not the first and last item in the underlying collection. For example:

Template

{% assign array = '1,2,3,4' | split: ',' -%}
{% for item in array limit: 1 -%}
a{{ item }} {{ forloop.first }} {{ forloop.last }}
{% endfor -%}
{% for item in array offset: continue -%}
b{{ item }} {{ forloop.first }} {{ forloop.last }}
{% endfor %}

Expected output

a1 true true
b2 true false
b3 false false
b4 false true

forloop.length behaves similarly.

Template

{% for item in (1..6) limit: 2 -%}
  a{{ item }}-{{ forloop.length }},
{%- endfor -%}
{% for item in (1..6) offset: continue -%}
  b{{ item }}-{{ forloop.length }},
{%- endfor -%}

Expected output

a1-2,a2-2,b3-4,b4-4,b5-4,b6-4,

You might also be interested in the test cases defined here. All of which pass using Ruby Liquid version 5.1.0.

Kindest regards, James

jg-rp avatar Jan 19 '22 17:01 jg-rp

Thanks @jg-rp for the test cases. Do you know what is the rule to relate loops between each others? For instance what if there is a loop on (1..6) then the next loop is on (5..9), should continue start from the previous index? Or does it have to match the same value to be a continuation?

sebastienros avatar Jan 19 '22 17:01 sebastienros

I believe the name of the loop variable and a string representation of the target collection or range are used to distinguish one loop from another.

Ruby Liquid uses a hyphen to separate the two. So an offset for {% for x in y limit: 2 %} would use the key "x-y", and {% for item in (1..6) limit: 3 %} would be "item-(1..6)".

Note that this approach disregards the contents of the target collection. Consider this template that reassigns foo between loops.

{% assign foo = '1,2,3,4,5,6' | split: ',' -%}
{% for item in foo limit: 3 -%}
  {{ item -}} 
{% endfor -%}
{% assign foo = 'u,v,w,x,y,z' | split: ',' -%}
{% for item in foo offset: continue -%}
{{ item -}}
{% endfor -%}

Output

123xyz

jg-rp avatar Jan 19 '22 18:01 jg-rp

Awesome. In my current implementation I am only doing it for identifiers, so this is something else to fix. More features than whatever people are using or aware of even of though.

sebastienros avatar Jan 19 '22 18:01 sebastienros

Related, there is a forloop.name property that should contain the token used by the loop.

https://github.com/harttle/liquidjs/commit/6dc7fada72467418806c1ee4bd7eaf3003690fe6

https://github.com/jg-rp/liquid/commit/e4e902429e40b85a0b6401d1637f8c09d615c253

sebastienros avatar Feb 06 '22 18:02 sebastienros