Capturing Item Index in Collection - ruby

I was wondering what is the proper way of finding the index of an item in an array is in a Liquid template, and selected related items based off of the index. Currently I'm able to calculate the value, but it seems to be a string and I can't then find other items in the array with the string. For example in a CMS:
{% for site_page in site.pages.all %}
{% if site_page.id == page.id %}
{% assign page_index = forloop.index0 %}
{% capture previous_page_index %}
{{ page_index | minus: 1 }}
{% endcapture %}
{% break %}
{% endif %}
{% endfor %}
The expected value can be found in previous_page_index (in this case 0) however, if i try to do something like site.pages.all[previous_page_index] I receive no output. If I do the same thing with a hardcoded index value: site.pages.all[0] it does yield an output. Does anyone have an idea/example on how this is supposed to be done in liquid?

Best I can figure is to use {% for item in array limit:1 offset:forloop.index0 %}. For example:
require 'liquid'
chars = %w[a b c]
names = %w[alpha bravo charlie]
puts Liquid::Template.parse(<<DONE).render( 'chars'=>chars, 'names'=>names )
{% for c in chars %}
{{c}} is
{% for n in names limit:1 offset:forloop.index0 %}{{n}}{% endfor %}
{% endfor %}
DONE
…which produces…
a is
alpha
b is
bravo
c is
charlie
Editorial aside: ouch. What an ugly tempting language. I understand its goals, but the burden it places on users is heinous.

Related

Making a custom collection in shopify liquid - liquid

I want to make a custom collection in collection.liquid based on some conditional scenario and for this I applied concat, append and join but the problem is that it returns ProductDropProductDropProductDropProductDrop... or LazyProductDropCollectionLazyProductDropCollection instead of products. Following is the code snippet
{% assign custom_products = '' %}
{% for product in collections["paneer-easy-indie-bowls"].products %}
{% assign custom_products = custom_products | append: product %}
{% endfor %}
instead of append I tried both join and concat but returns ProductDropProductDropProductDropProductDrop...
{% assign custom_products = custom_products | concat: product %}
then I tried the following:
{% capture custom_products %}
{% for product in collections["paneer-easy-indie-bowls"].products %}
{{ custom_products }},{{ product.handle }}
{% endfor %}
{% endcapture %}
{% assign custom_products = custom_products | split: ',' %}
{% for product in custom_products %}
{{ product}}
{% endfor %}
but this code not appending the products in right way. I want products like same as{{collection.products}}. Any suggestion ?
If I read your pseudo-code correctly, you are trying to build a collection of products from a collection of products. Which leads to the question, why? Since you already have a perfect collection, use it as is!

ansible jinja2 - how to use "loop range" output in other "for statement"

I want to use the n values in the next for statement and I expect output as router_0, router_1. But any optisons like router_[n], router_{{ n }}, router_(n) etc didnt work. How can we do this?
{% for n in range(0, 2) %}
{% for rtr in web.router_[n] %}
{% if rtr.interface.type == 'lacp' %}
interface Port-Channel{{ rtr.interface.id }}
.
{% endif %}
{% endfor %}
{% endfor %}
In vars.yaml, there are variables for paired routers, router_0 and router_1. I want to generate the config template for both routers at the same time.
Thanks,
Concatenate the name of the attribute
{% for rtr in web['router_' ~ n] %}

Calculate needed spaces in Jinja2 / Ansible [duplicate]

This question already has an answer here:
Pushing items to a var while looping over another var in Ansible Jinja2 template
(1 answer)
Closed 4 years ago.
I'm trying to calculate the needed space based on the longest word in a dictionary.
It seems though that the variable num doesn't transfer it's value to the second inner loop.
I'm basically trying to caculate the amount of spaces to align the columns correctly.
{% for module in modules %}
module "{{ module.name }}" {
source = "{{ module.source }}"
{% set num = 1 %}
{% for n in module.vars.keys() %}
{% if num < n|length %}
{% set num = n|length %}
{% endif %}
{{ num }}: {{ n }}
{% endfor %}
{% for m in module.vars %}
{{ num }}
{{ m }} {{ '= "' + module.vars[m]|indent(width=num) }}"
{% endfor %}
}
You're right, you can't get variables out of loops this way. See "Scoping behavior" in the docs.
One option is to use what they suggest and create a namespace:
{% set ns = namespace(num=0) %}
{% for n in module.vars.keys() %}
{% if ns.num < n|length %}
{% set ns.num = n|length %}
{% endif %}
{{ ns.num }}: {{ n }}
{% endfor %}
In your case, there is an easier and cleaner solution though: you can calculate the maximum width in an expression. Use map() to get a list of lengths, and use max filter to get the biggest one:
{% set indent_width = module.vars.keys() | map("length") | max %}
{% for m in module.vars %}
{{ m }} {{ '= "' + module.vars[m]|indent(width=indent_width) }}"
{% endfor %}

Using Jinja2 Expression within Jinja2 Statement

Let's say I have the following Jinja2 variables:
'dev_ami' = 'ami-123456'
'dev_located_ami' = 'ami-123456'
'prod_ami' = 'ami-654321'
'prod_located_ami' = 'ami-654321'
I would like to set a condition upon when the 'dev_ami' variable is equal to the 'dev_located_ami' variable. This would easily be done as shown in the following statement:
{% if dev_ami == dev_located_ami %}
... do some stuff
{% else %}
... do some other stuff
{% endif %}
But I would like to dynamically compare amis based on the deployment environment contained in a list ['dev','prod', etc...]. The following contains a templating error since there is an expression within a statement as such - {% {{ .. }} %}:
{% for env_type in ['dev','prod'] %}
{% if {{ env_type }}_ami == {{ env_type }}_located_ami %}
... do stuff
{% else %}
... do other stuff
{% endif %}
{% endfor %}
I have tried to set variables to represent the expressions I would like in the following code but unfortunately they are compiled literally as 'dev_ami' and 'dev_located_ami' whereas I would like them compiled to their corresponding variable values 'ami-123456' and 'ami-123456':
{% for env_type in ['dev','prod'] %}
{% set ami = "%s_ami"|format(env_type) %}
{% set located_ami = "%s_located_ami"|format(env_type) %}
{% if ami == located_ami %}
... do stuff
{% else %}
... do other stuff
{% endif %}
{% endfor %}
I have checked through various filters and so far have had no success. Would appreciate advice on getting this specific implementation to work properly. Thank you in advance.
I think you might be approaching the problem with the wrong datastructure in mind.
Dynamically generating variable names in order to compare amis in different environemts sounds like a massive overkill to me. Are you familiar with lists & dictionaries?
Try to start from something like this (pseudocode):
dict = { environments:
prod:
ami1: foo
ami1_located: foo
dev:
ami1: bar
ami1_located:baz
}
for env in dict[environments]:
if env[ami1] == env[ami1_located]:
[...]

Create a filter list of items where a filter item appears only if there is a story for the product

I'm trying to figure out how to create a filter list where it grabs the product title from a _products directory and it will show the title in the list only if there is a story with the title of the product.
I have a _products list with 11 items and I can show them all, but I want to remove an item from the list of filter items if it's title != a element in the front matter of a list of stories in a separate directory.
{% assign product = site.products %}
{% for product in site.products %}
{% for success in site.success-stories %}
{% if product.title == success.product %}
<li><p class="filter label label-default" data-filter=".{{product.title | downcase}}">{{product.title}}</p></li>
{% endif %}
{% endfor %}
{% endfor %}
What this does is shows only the items that have a matching success-story but if there are multiple stories with the same product in it's front matter it will show that product more than once.
I've got the filtering down, it's just showing the items I can't get down.
Thanks!
I have a feeling you are looking for the contains filter.
Taken directly from the Liquid for Designers wiki:
# array = 1,2,3
{% if array contains 2 %}
array includes 2
{% endif %}
In your case, I'm guessing you want:
{% assign product = site.products %}
{% assign successfulProducts = site.success-stories | map: 'product' %}
{% for product in site.products %}
{% if successfulProducts contains product.title %}
... {{ product }} ...
{% endif %}
{% endfor %}
All I've done here in line 2 is mapping success-stories to successfulProducts. Essentially each story element is replaced by its story.product property.
Then I loop through all the products like you and check each one if it should be displayed. But I don't loop through all the stories - instead I put a simple condition that checks if successfulProducts contains the product title, just as you specified.

Resources