On Jekyll (Liquid) can't get where working - where-clause

I have a people.yml like
- name: Foo
activities:
- title: bar1
- title: bar2
And an assign like
{% assign person = site.data.people | where: "activities.title", "bar1" %}
When I previously had only one activity per person (without having the title attribute) I could easily make it work. But now I'm struggling with it.

You cannot pass an Array to the where filter. It will not try to find the desired value by looping over all Hashes like {"title"=>"bar1"}, it will simply evaluate the property against the passed string. So, those Hash will never be equal to bar1.
My two cents :
Simplify people.yml by removing activities.name key :
Note : the two activities array presentations are equivalent.
- name: Foo
activities: [ bar1, bar2 ]
- name: Bar
activities:
- bar3
- bar4
You can now use the contains filter to retrieve people that have bar1 as an activity. contains filters strings or array like ["bar1", "bar2"]
{% assign selected = "" | split: "/" %} --> create empty array
{% for people in site.data.people %}
{% if people.activities contains 'bar1' %}
--> add people to array if match is found
{% assign selected = selected | push: people %}
{% endif %}
{% endfor %}

Related

Shopify: How to check if there is > 1 collection in a cart

I am trying to prevent orders with more than one collection from being checked out. If there's more than one collection, i just want to disable the checkout button. I know the syntax is bad, but i want to do something like this.
{% for item in cart.collections %}
{% if item.collection.length > 1 %}
<button disabled>Cant checkout</button>
{% else %}
<button disabled>Can checkout</button>
{% endif %}
This is the current code for the button
<div class="cart__checkout-wrapper">
<button disabled id="button" type="submit" name="checkout" data-terms-required="{{ settings.cart_terms_conditions_enable }}" class="btn cart__checkout">
{{ 'cart.general.checkout' | t }}
</button>
{% if additional_checkout_buttons and settings.cart_additional_buttons %}
<div class="additional-checkout-buttons additional-checkout-buttons--vertical">{{ content_for_additional_checkout_buttons }}</div>
{% endif %}
</div>
{% endfor %}
Method 1
This simple check might work for what you need:
{% liquid
assign all_collections_in_cart = cart.items | map: 'product' | map: 'collections' | map: 'title' | uniq
assign collection_count = all_collections_in_cart.size
if collection_count > 1
assign can_checkout = false
else
assign can_checkout = true
endif
%}
What it does
First, we use a sequence of map filters to 'drill down' through the object structure. Items in the cart do not themselves belong to any specific collection(s), but each item is associated with both a variant and a product. From the product, we can then get all of the collections that it belongs to, which will be an array of collection objects. We can't do much with the objects themselves, so we drill down one layer further to get something that can be compared, such as title handle or id. At this point we now have an array of text entries (or numbers, if you swap to IDs), so we can apply the uniq filter to remove any duplicates.
Now that we have an array with no duplicates, we can check the size of the array to see if it is greater than one and set a variable appropriately.
Note that this will always return false if a single product belongs to more than one collection!
Method 2:
If instead you need to test that all products share a collection because products can belong to more than one at a time, we won't be able to take as many shortcuts. Instead, our code might look something like this:
{% liquid
assign can_checkout = true
if cart.items.size > 1
assign shared_collections = cart.items.first.product.collections | map: 'title'
for item in cart.items
if forloop.first
continue
endif
assign item_collections = item.product.collections | map: title
assign placeholder_array = ''
for coll in shared_collections
if item_collections contains coll
if placeholder_array.size > 0
assign placeholder_array = placeholder_array | append: ','
endif
assign placeholder_array = placeholder_array | append: coll
endif
endfor
assign shared_collections = placeholder_array | split: ','
if shared_collections.size == 0
assign can_checkout = false
break
endif
endfor
endif
%}
What it does
This second version will trigger if there are 2 or more items in the cart. If there are, we get all of the collection titles from the first item in the cart, then loop through all of the remaining items in the cart. At each step, we go through our list of shared collections to see how many are shared with the current item in the cart to make a new array. (Liquid does not let us make an array directly, so we have to build out a delimited string and then use the split filter afterwards)
If at any time we have an array with 0 shared entries, we set our can_checkout variable to false and use the break command to stop searching.
Addendum
For both of the above methods, things might get a little bit trickier if you have certain collections that don't count towards your collection rules. For example, if you have 'featured' or 'summer sale' collections, you will want to make sure that you aren't counting those in the collection comparisons (for example, by having an array of exceptions and removing those items from the array of collections that the item belongs to).

How to verify if the input is numeric or string in liquid template?

I will receive input values with two combinations either numeric or string value. If input is number you need to map it to one output field and if the input is string map to other field.
Eg, Input:
[{
"Input": "ABC123"
},
{
"Input": "12345"
}]
Output:
[{
"String": "ABC123"
},
{
"Number": "12345"
}]
You can use the json filter to determine the type since it will wrap a string in quotes, but not numbers. Check for the " character and you'll know if it's a number. Will also cover your edge case.
{% assign var1 = "1" %}
{% capture testValue %}{{ var1 | json }}{% endcapture %}
{% if testValue contains '"' %}string{% else %}number{% endif %}
Dotliquid does not have this functionality at this moment. It is open source project and you can download source code and change/Add new method in StandardFilters.cs. You can then use it for fulfil your requirement. you can add as many operation you like for your custom requirement.
e.g
Add below method in StandardFilters.cs
public static bool IsNumeric(object o)
{
return double.TryParse(Convert.ToString(o), out double result);
}
you can use this method in your liquid template like,
{% assign string = '' %}
{% assign number = '' %}
{% assign isNumber = model.Input | IsNumeric %}
{% if isNumber == true %}
{% assign number = model.Input %}
{% else %}
{% assign string = model.Input %}
{% endif %}

How to use a variable as a key in a hash in liquid (Shopify)

I would like to set up a hash variable using strings from an array dynamically (instead of writing 1000 lines of code).
As well I would like to use a dynamically created string to access a hash by using it as the key - for a built-in (what I assume is a hash) object - settings. Settings allow you to access data in settings_schema.json, eg: settings.my_custom_setting
According to this documentation: https://github.com/Shopify/liquid/wiki/Liquid-for-Designers
"For hashes, the key must be a literal quoted string or an expression that resolves to a string."
so I have tried {% assign filter[thisFilter] = false %} but get an error: ParseError: illegal token
First issue / accessing hash key with a variable:
{% comment %} All possible filters {% endcomment %}
{% assign allFilters = "color,size,collection,style,height,function,artist" %}
{% assign allFiltersArray = allFilters | split ',' %}
{% comment %} hash of filters each set to false {% endcomment %}
{% for thisFilter in allFiltersArray %}
{% assign filter[thisFilter] = false %}
{% endfor %}
Second issue, accessing settings object with a dynamically generated key:
{% comment %} set to true whichever filters are configured in settings for this collection {% endcomment %}
{% for thisCollection in allCollectionsArray %}
{% if thisCollection == currentCollection %}
{% for thisFilter in allFiltersArray %}
{% assign itemToCheck = "filter_" | append: thisCollection | append: "_" | append: thisFilter %}
{% if settings[itemToCheck] %}
{% assign filter[thisFilter] = true %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
In the first issue, I expect the result to be a hash such as:
filter['color'] = false (or filter.color = false)?
filter['size'] = false
In the second issue, I'm expecting something like:
{% if settings.filter_shirts_color %}
What you are trying to do is not possible. If you read further on your provided link Liquid for Designers, it is mentioned
Note that there is no way to write a literal array or hash as an
expression; arrays and hashes must be passed into the template, or
constructed obliquely with a tag or output statement.
Moreover, even if you have such hash, you cannot assign it new value. For example,
{% assign settings['some-setting-id'] = false %}
This will not work. Same is the case with array created using split filter. You cannot assign new values on any index.
For the second issue, this should work, the error in your case most probably is because of invalid generated string or there is no setting with that id. This should work fine and display value for that setting.
{%assign string_key = 'setting-key'%}
{{settings[string_key]}}
But this will not work because
{%assign string_key = 'setting-key'%}
{{settings.string_key}}
my_hash.key — Hashes also allow a shorter "dot" notation, where the
name of the variable is followed by a period and the name of a key.
This only works with keys that don't contain spaces, and (unlike the
square bracket notation) does not allow the use of a key name stored
in a variable.

Shopify (Liquid): Syntax error expected close_square but found comma

I am creating an array in Shopify (Liquid) and I get an error,
{% assign numbers = [
"One",
"TWo",
"three",
"bla"
]
%}
Line 126 — Liquid syntax error: Expected close_square but found comma
in "{{[ "One","TWo", "three","bla" ] }}"
There is no way to create an array like this in liquid.
Instead, you can use the split filter to create an array from a string.
{% assign numbers = "one,two,three,four" | split: "," %}
<pre>{{ numbers | inspect }}</pre>
You can also create an empty array and feed it with the push filter
{% comment %} +++ Creates an empty array +++ {% endcomment %}
{% assign numbers = "" | split: "" %}
<pre>{{ numbers | inspect }}</pre>
{% comment %} +++ Feed the beast +++ {% endcomment %}
{% assign numbers = numbers | push: "one" %}
<pre>{{ numbers | inspect }}</pre>
{% assign numbers = numbers | push: "two" %}
<pre>{{ numbers | inspect }}</pre>

Liquid - Convert Array to Lowercase

I'm using Shopify and want to hook into customer tags, however they are case sensitive. So {% if customer.tags contains "wholesale" %} is not the same as {% if customer.tags contains "Wholesale" %}. My client may or may not stick to one case when applying tags so I want to guard against that in the future.
I would like to take an array, customer.tags, and convert all of the values to lowercase. I'm trying to work out the logic but am having trouble.
I want to put customer.tags into a new array which isn't working.
{% assign newArray = customer.tags %}
{{ newArray }}
What am I doing wrong?
You could use the downcase filter for this:
{% assign contains_wholesale = false %}
{% for tag in customer.tags %}
{% assign lowercase_tag = tag | downcase %}
{% if lowercase_tag == 'wholesale' %}
{% assign contains_wholesale = true %}
{% endif %}
{% endfor %}
Note: downcase just works for ASCII characters. If you need to search for strings with accented letters or other Unicode characters then this won't be enough.
if you would like to keep customer.tags as an array so you can keep using contains in a simple if statement (like your example). You could also chain a couple of liquid filters together to turn all of the strings within the array to lowercase.
Example:
{% assign lowercaseTags = customer.tags | join: ',' | downcase | split: ',' %}
{% assign randomString = 'WholeSale' | downcase %}
{% if lowerCaseTags contains randomString %}
{% comment %}
Will now match regardless of case sensitivity
{% endcomment %}
{% endif %
Explanation:
Join filter: Turn the array into a string seperated by ,
Downcase filter: make the whole string lowercase
Split filter: opposite of join, recreates the array from the string based on the character used in join ,
Another solution as you use the "contains" operator would be to skip the "w".
Something like {% if customer.tags contains 'holesale' %} should work.