Twig for loop - output only parent if any children exists - loops

These lines prints all categories and the products within. But if the category has no products the category title is still printed.
{% for category in categories %}
<h1>{{ category.title }}</h1>
{% for product in products if products.category|first == category.title|lower %}
<h2>{{ product.title }}</h2>
{% endfor %}
{% endfor %}
How can I optimize this so category titles are printed only when the category contains products?

There are (imo) 2 solution for this. The first one is pure twig-based, but is the worst of the two methods.
In order to skip categories without children, this would require a second loop and flag variable to determine wether to skip the category or not. As twig can't break out of loops this means u would need to foreach the products twice completely
{% for category in categories %}
{% set flag = false %}
{% for product in products if products.category|first == category.title|lower %}
{% set flag = true %}
{% endfor %}
{% if flag %}
<h1>{{ category.title }}</h1>
{% for product in products if products.category|first == category.title|lower %}
<h2>{{ product.title }}</h2>
{% endfor %}
{% endif %}
{% endfor %}
The second and better solution is just to add an extra method to your category-model and do something like
{% for category in categories if category.hasProducts() %}
<h1>{{ category.title }}</h1>
{% for product in products if products.category|first == category.title|lower %}
<h2>{{ product.title }}</h2>
{% endfor %}
{% endfor %}

An easy solution would be to add a is defined condition to your twig loop.
Example:
{% for category in categories if categories is defined %}
<h1>{{ category.title }}</h1>
{% for product in products if products.category|first == category.title|lower %}
<h2>{{ product.title }}</h2>
{% endfor %}
{% endfor %}

Related

Liquid Loop prints duplicates

I'm editing a "freshdesk knowledgebase" theme which uses (parts of?) liquid. I don't have a lot of experience.
The knowledgebase uses a structure of category: -> folder -> article
Here's the loop i'm having trouble with. While it outputs the list of available categories, it also prints a duplicate depending on how many articles are inside the folder.
<div class="category-list__items">
{% for category in portal.solution_categories%}
{% for folder in category.folders %}
{% if folder.articles_count > 0 %}
<div class="category-list-item">
<a href="{{category.url}}" class="category-list-item__link">
<div class="category-list-item__content">
<h3 class="category-list-item__title">{{category.name}}({{ folder.articles_count }})</h3>
</div>
</a>
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
What I am wanting to output is a just a list of categories that have at least 1 article inside.
I'm getting:
Fruits (2)
Fruits (2)
Vegetables (1)
When I just want:
Fruits (2)
Vegetables (1)
You can use {% break %} to break the for loop conditionally.
So if you want to render a category only once when it finds a first folder with articles_count > 0 then break the loop and continue for next category like below.
<div class="category-list__items">
{% for category in portal.solution_categories%}
{% for folder in category.folders %}
{% if folder.articles_count > 0 %}
<div class="category-list-item">
<a href="{{category.url}}" class="category-list-item__link">
<div class="category-list-item__content">
<h3 class="category-list-item__title">{{category.name}}({{ folder.articles_count }})</h3>
</div>
</a>
</div>
{% break %}
{% endif %}
{% endfor %}
{% endfor %}
</div>

Shopify loop - Skipping a product variant if variant has no image

I need to do two things; add products with extra colours to the collections loop (which I've done below, via a snippet elsewhere), however I need to skip when the variant doesn't have its own image which I've yet to figure out.
The break I attempted is in the preview code:
{% for option in product.options %}
{% if option == 'Colour' %}
{% assign index = forloop.index0 %}
{% assign colourlist = '' %}
{% assign colour = '' %}
{% for variant in product.variants %}
{% capture colour %}
{{ variant.options[index] }}
{% endcapture %}
{% if variant.image.src %}
{% break %}
{% endif %}
{% unless colourlist contains colour %}
{% include 'product-grid-item' %}
{% capture tempList %}
{{colourlist | append: colour | append: " " }}
{% endcapture %}
{% assign colourlist = tempList %}
{% endunless %}
{% endfor %}
{% endif %}
{% else %}
<div class="grid-item">
<p>{{ 'collections.results.no_products' | t }}</p>
</div>
{% endfor %}
{% endfor %}
The keyword you're looking for, to skip the current iteration of the loop and move to the next, is {% continue %}
For example:
{% for variant in product.variants %}
{% if variant.featured_image == blank %}
{% continue %}
{% endif %}
<!-- HTML STUFF -->
{% endfor %}

Checking for tag duplicates with jekyll excludes tags that are contained in other tags

I am trying to build a (simple, unweighted) tag cloud with liquid in my jekyll site. The tag cloud renders fine when I use this code:
{% assign sitetags = "" %}
{% for page in site.pages %}
{% for tag in page.tags %}
{% unless sitetags contains tag %}
{% assign sitetags = sitetags | append:tag | append:', ' %}
{% endunless %}
{% endfor %}
{% endfor %}
{% assign sitetags = sitetags | split:', ' | sort %}
{% for tag in sitetags %}
{% capture tag_name %}{{ tag }}{% endcapture %}
<div>
{{ tag_name }}
</div>
{% endfor %}
However, there is one issue: if the name of a tag is contained within another tag, it won't show up in the tag cloud. For example, "art" is contained in "art-history" so it does not show up. In order to deal with this, I tried to fix it, however, it is not working. Here is my code:
{% assign sitetags = "" %}
{% for page in site.pages %}
{% for tag in page.tags %}
{% if sitetags contains tag %}
{% assign sitetags = sitetags | split:', ' %}
{% assign truetag = true %}
{% for taggo in sitetags %}
{% if taggo != tag %}
{% continue %}
{% else %}
{% assign truetag = false %}
{% break %}
{% endif %}
{% endfor %}
{% if truetag == true %}
{% assign sitetags = sitetags | append:tag | append:', ' %}
{% endif %}
{% else %}
{% assign sitetags = sitetags | append:tag | append:', ' %}
{% endif %}
{% endfor %}
{% endfor %}
{% assign sitetags = sitetags | split:', ' | sort %}
{% for tag in sitetags %}
{% capture tag_name %}{{ tag }}{% endcapture %}
<div>
{{ tag }}
</div>
{% endfor %}
The reason I am attempting it this way as opposed to just using site.tags is because I have articles/essays in other directories that I am trying to include. When I use site.tags, it only includes the tags that are on the blog posts but not the essays. Essentially, my site has both regular blog posts under /_blog and also essays in a different location, but I want to aggregate the tags of all of these in the same tag cloud.
I would really appreciate advice on what I am doing wrong or how to fix it. Thanks!
Instead of using a string to store your tags, you can use an array.
{% assign sitetags = "" | split:"" %}
{% for page in site.pages %}
{% if page.tags.size %}
{% assign sitetags = sitetags | concat: page.tags %}
{{ sitetags | inspect }} <br>
{% endif %}
{% endfor %}
{% assign sitetags = sitetags | sort | uniq %}
{{ sitetags | inspect }} <br>
{% for tag in sitetags %}
<div>
{{ tag }}
</div>
{% endfor %}
I was able to figure this out myself actually. Part of the issue was in initializing the array, so I ended up working with a string instead because I just couldn't get the arrays to work for some reason. I skipped over initialization and went straight to adding items to it. After that all I had to do was just run the uniq filter and it filtered out all the duplicates, so I didn't have to do any string comparison or anything like that.
Here is the solution:
{% for post in site.pages %}
{% for tag in post.tags %}
{% assign tagcloud = tagcloud | append:tag | append:', ' %}
{% endfor %}
{% endfor %}
{% assign tagcloud = tagcloud | split:", " | uniq | sort %}
{% for tag in tagcloud %}
{% capture tag_name %}{{ tag }}{% endcapture %}
<div>
{{ tag_name }}
</div>
{% endfor %}

How to Display key labels and values in Jekyll for loop data that has been grouped

I got the following to work:
{% assign cars = site.data.inventory %}
{% for item in cars %}
{{item}}
{% endfor %}
The result looks like a jumble of key pairs.
{"brand"=>"Toyota", "model"=>"Celica"}
etc.
All good. Then I threw it a curve ball.
{% assign cars = site.data.inventory | group_by:"model" %}
Now the result looks different, and that makes sense, but it's causing confusion.
{"name"=>"Celica","items"=>[{"brand"=>"Toyota", "model"=>"Celica"}], "size"=>1}
Where this becomes a challenge:
I want to iterate through the "items" in the 2nd result, pulling only the key labels. If I remove the grouping, I can do this:
{% for e in cars %}
{% if forloop.first == true %}
{% for item in e %}
{{item[0]}}
{% endfor %}
{% endif %}
{% for item in e %}
{{item[1]}}
{% endfor %}
{% endfor %}
Works like a charm. The first result is the label, the remaining results show the data. A simple way to make a column header.
However, if I add the group_by in the assign, my {{item[0]}} becomes "name", "items", "size", instead of "brand", "model" where items becomes the entire list {{item}} from the first example.
How do I iterate through the values in bold?
[{"brand"=>"Toyota", "model"=>"Celica"}] while they are grouped?
I think that's what you're trying to do :
{% assign models = site.data.inventory | group_by:"model" %}
<table>
{% for model in models %}
{% if forloop.first == true %}
<tr>
{% for item in model.items.first %}
<th>{{ item[0] }}</th>
{% endfor %}
</tr>
{% endif %}
{% for car in model.items %}
<tr>
{% for field in car %}
<td>{{ field[1] }}</td>
{% endfor %}
</tr>
{% endfor %}
{% endfor %}
</table>

Jekyll and Liquid - Filter redundant entries in list

i would like to generate a list of posts ordered by a variable in the posts without redundant entries.
yaml:
---
time: 15 minutes
---
liquid:
{% for post in site.posts %}
{% capture currentTime %}{{ post.time }}{% endcapture %}
{% if currentTime %}
<div class="sublist-italic">{{ currentTime }}</div>
{% endif %}
{% if post.category == 'recipe' %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
The result:
How can i put all values of a post variable in an array, filter the duplicates and assign the corresponding post titles?
Thanks for your help!

Resources