Using Liquid, is there a way to filter an array and ignore non matching items - arrays

Using Liquid for a store.
I want to look through an array of values and check to see if any of those values match another list of values. If they match i want to display them. if they do not match i want to ignore them.
I'm currently able to do it in reverse by looking at all the values then 'remove' the ones i don't want individually but that is a terrible way to do this.
{% for 'field' in [metafield.key.value] | split: ", " %}
{% if field == 'value 1' or field == 'value 2' or field == 'value 3'%}
<div>
field
</div>
{% else %}
{% continue %}
{% endif %}

for future weary travelers:
this works you may need to adjust where you're splitting the string:
{% assign metafield_str = product.metafields.yada.yada | split: "," %}
{% assign control_str = 'value, something, test, test2' | split: "," %}
{%- capture variable -%}
{% for c_str in control_str %}
{% for m_str in metafield_str %}
{% if c_str == m_str %}
{{m_str}}
{% endif %}
{% endfor %}
{% endfor %}
{%- endcapture -%}

Related

How to sort product tags based on another array in Shopify liquid

I have an issue where I need to display tags in a specific order, based on the first part of the tag. For example, I have a list of potential product tags:
category_electronics
size_large
color_black
color_brown
weight_10
And I need them to sort based on a pre-set order applied to the first part of the tags (before the underscores), for example:
size
color
weight
category
So with the above example, I'd want to end up with the list sorted like:
size_large
color_black
color_brown
weight_10
category_electronics
If the full tag names were all known, this would be simple, as I could just do something like:
{% assign tags = 'size_large,color_black,color_brown,weight_10,category_electronics' | split: ',' %}
{% for t in tags %}
{% assign tag = t | strip %}
{% if product.tags contains tag %}
<li>{{ tag }}</li>
{% endif %}
{% endfor %}
{% endif %}
However, that is not the case. The value after the underscore in the tag is variable, and there could be multiple occurrences of any of the tag groups (ie. size above) for any single product.
It seems I'd need to use some kind of associative array to make this happen, but I can't quite wrap my head around how that would be done.
The following should work:
{%- assign tag_groups = "size,color,weight,category" | split: "," -%}
{%- assign sorted_tags = "" -%}
{%- for group in tag_groups -%}
{% for tag in product.tags %}
{%- assign tag_group = tag | split: "_" | first -%}
{%- if tag_group == group -%}
{%- assign sorted_tags = sorted_tags | append: tag | append: "," -%}
{%- endif -%}
{% endfor %}
{%- endfor -%}
{%- assign sorted_tags_arr = sorted_tags | remove_last: "," | split: "," -%}
{%- for tag in sorted_tags_arr -%}
{{- tag -}}<br>
{%- endfor -%}

Loop through products in collection and find lowest price (excluding $0 prices)

I have a Shopify site with multiple categories. Some products do not have a price (Shopify interests this as $0). All collections are displayed on the front page. I want to loop through all products in each collection and output the minimum price (excluding $0 values).
The current array I'm using is:
{% assign startingFrom = collection.products | sort: 'price' %}
{{ startingFrom[0].price_min | money_without_currency }}
Unfortunately, this captures and outputs $0 values.
What is the best way to loop through values that are higher than $0 and output the lowest price?
I have tried to exclucde zero values:
{% assign startingFrom = collection.products | sort: 'price' %}
{% if 'price' != 0.00 %}
{{ startingFrom[0].price_min | money_without_currency }}
{% endif %}
This outputs $0.00.
{% for product in collection.products %}
{% if forloop.first %}
{% assign lowest_price = product.price %}
{% endif %}
{% assign new_price = product.price %}
{% if new_price < lowest_price and new_price != 0 %}
{% assign lowest_price = new_price %}
{% endif %}
{% endfor %}
it is not tested but it should work.
You can't rely on that way unless you have fewer than the pagesize limit of products in your collection.
See the discussion and solution under Shopify: Getting highest price item from collection

How do I create an array from a forloop?

I have a folder of images that I'd like to render on a page. I'd like these images to be ordered/filtered a particular way. To do that, I understand that the images need to first be together in an array.
I therefore start with an empty array:
{% assign my_array = "" %}
I then loop through the image folder, and attempt different ways of pushing each image into my_array. Example:
{% for image in site.static_files %}
{% if image.path contains "assets/images/target-folder" %}
<!-- Push image into array -->
{{ my_array | push: image }}
{% endif %}
{% endfor %}
Ideally, I can then use this array as intended:
{% for image in my_array | sort:"date" | reverse %}
<!-- show image -->
{% endfor %}
I'm aware that I could make a data file with the images, but I'd like to avoid needing to take that extra step. Thanks for reading.
You are almost there, the way of how you are creating the array it is the only thing to fix.
This {% assign my_array = "" %} creates an empty string. One easy way to create an array in Liquid is to split the above:
{% assign my_array = "" | split: ',' %}
Now you can push items into the array inside a for loop in the following way:
{% for image in site.static_files %}
{% if image.path contains "assets/images/target-folder" %}
<!-- Push image into array -->
{% assign my_array = my_array | push: image %}
{% endif %}
{% endfor %}
Also note that you can do this without a loop using where/where_exp filters:
{% assign my_array = site.static_files |
where_exp: "item", "item.path contains 'assets/images/target-folder'" %}
or:
{% assign target_folder = "assets/images/target-folder" %}
{% assign my_array = site.static_files |
where_exp: "item", "item.path contains target_foler" %}
(Although, unlike the accepted answer, this doesn't precisely correspond to the question's title, it's still a useful option in the described example.)
This solution worked for me:
{% assign namesArr = '' %}
{% for animal in animals %}
{% assign namesArr = namesArr | append: animal.name %}
{% if forloop.last == false %}
{% assign namesArr = namesArr | append: "," %}
{% endif %}
{% endfor %}
{% assign namesArr = namesArr | split: "," %}
Now namesArr is array, we can check array contains some value: https://stackoverflow.com/a/30823063/5638975

Jekyll forloop.last --> before last?

I made a dummy "related posts" in Jekyll 3.2.1 with the following solution:
{% for post in site.posts limit: 4 %}
{% if page.author == post.author and page.title != post.title %}
<div class="post-stream-item">
<a href="{{ post.url | prepend: site.baseurl }}" title="{{ post.title }}"><div class="post-stream-content">
<img src="{{ post.thumbnail }}" width="80" height="auto" /><span class="post-stream-item-meta"><h3>{{ post.title }}</h3><p>{{ post.author }} on {{ post.date | date: "%b %-d, %Y" }} • {% assign words = post.content | number_of_words %}
{% if words <= 160 %}
1 min
{% else %}
{{ words | plus: 159 | divided_by:160 }} mins
{% endif %} read</p></span></div></a></div>{% if forloop.last == false %}<hr>{% endif %}
{% endif %}
{% endfor %}
The for loop iterates through the posts list in the site and gives
it a limit
If the author of the current post is the same as the author of the
iterated post, but the title is not the same, then it fills out the
jinja bindings.
The problem is with the {% if forloop.last == false %}<hr>{% endif %} part, cause if there is more iterable (post) in the forloop, it will display the <hr> tag, even if it's the last element shown to the user.
Is there any attribute to refer to the penultimate element of the list or any better solution to this?
There isn't going to be a simple out-of-the-box one-line solution for this. Think about it this way: you're essentially asking for a feature that looks ahead in time and figures out whether this is the last time the if statement will evaluate to true. Jekyll is great, but it can't predict the future!
You could do this yourself by using two loops: one that loops through and counts how many <hr> elements you should show. Then another that actually prints stuff out, checking against the count you came up with to decide whether to print the <hr> element.
Or you could just use CSS to hide the last <hr> element. Google is your friend here.
Print a certain number of posts with no print condition
Solution : use loop limit
{% for post in site.posts limit: 4 %}
... output code here
{% endfor %}
You will print exactly 4 posts and forloop.last always works.
Print a certain number of posts with a print condition in the loop
Solution : use where filter, a counter and break
Now that you include a conditional printing :
you don't know which and how many posts will be printed.
if you don't print last post, you have an HR at the end of your list.
If you want to know how many posts you can print, you can use {% assign authorsPosts = site.posts | where: "author", page.author %} and authorsPosts.size.
This code will do it nicely, even if available posts number is less than your limit.
{% comment %} +++++ Max number of posts to print +++++ {% endcomment %}
{% assign limit = 4 %}
{% comment %} +++++ Select authors posts +++++{% endcomment %}
{% assign authorsPosts = site.posts | where: "author", page.author %}
{% comment %} +++++ If author's Posts number is less than limit, we change the limit +++++ {% endcomment %}
{% if limit >= authorsPosts.size %}
{% comment %} +++++ Number of "listable" posts is author's posts number less 1 (the one actually printed) +++++ {% endcomment %}
{% assign limit = authorsPosts.size | minus: 1 %}
{% endif %}
{% assign postsCounter = 0 %}
{% for post in authorsPosts %}
{% if page.author == post.author and page.title != post.title %}
{% assign postsCounter = postsCounter | plus: 1 %}
<h3>{{ post.title }}</h3>
{% comment %} +++++ Prints hr only if we are not printing the last post +++++ {% endcomment %}
{% if postsCounter < limit %}<hr>{% endif %}
{% comment %} +++++ Exit for loop if we reached the limit +++++ {% endcomment %}
{% if postsCounter == limit %}{% break %}{% endif %}
{% endif %}
{% endfor %}

How to add a new index to an existing array on Volt?

So I have an existing array, I want to run a for loop through it and recreate new arrays. I am trying to figure out how to create my own array directly on volt. Here is my code:
{% set oNomesAgendaAmigos = [], oNomesAgendaRecomendado = [], oNomesAgendaAmigosRecomendado = [] %}
{% for oNomeAgenda in oNomesAgenda %}
{% set oNomesAgendasTotal = oNomeAgenda.cliente_nome %}
{% if oNomeAgenda.ind_amigo == 1 %}
{% set oNomesAgendaAmigos = oNomeAgenda %}
{% endif %}
{% if oNomeAgenda.ind_recomendado == 1 %}
{% set oNomesAgendaRecomendado = oNomeAgenda.cliente_nome %}
{% endif %}
{% if oNomeAgenda.ind_recomendado == 1 AND oNomeAgenda.ind_amigo == 1 %}
{% set oNomesAgendaAmigosRecomendado = oNomeAgenda.cliente_nome %}
{% endif %}
{% endfor %}
Last time i have checked there were no mechanism for setting table bit by bit in Volt. The walk-around would be to use array_merge() or implement own filter/method into Volt engine.
Anyway it's a bit against MVC principles. You should set all tables you need over your PHP part of code.
To loop ever array with indexes inside loop you use that trick:
{% for index, value in numbers %}
{{ index }}: {{ value }}
{% endfor %}
I also really appreciate this part of Volt Documentation.

Resources