How to concatenate / append a string to another one in Jekyll / Liquid? - jekyll

To be clear, assuming:
{% assign my_var = "123" %}
{% assign another_var = "456" %}
I would like to append string to my_var to get something like 123 - 456
What I have tried so far:
{% assign my_var = my_var + " - " + another_var %}

You could use the capture logic tag:
{% capture new_var %}{{ my_var }} - {{ another_var }}{% endcapture %}
It is also possible to use the append filter, as Ciro pointed:
{% assign new_var = my_var | append: ' - ' | append: another_var %}

append: filter
This is more convenient than capture for short concatenations:
{% assign x = 'abc' %}
{% assign y = 'def' %}
{% assign z = x | append: ' - ' | append: y %}
{{ z }}
Output:
abc - def
Tested on jekyll 3.0.4 (github-pages 75).

All the answers so far are correct, but they fail to mention that you can also inline the append instead of having to assign a new variable:
Link

Related

String manipulation in DBT - Jinja

I want to develop a macro that will loop rows from a seeding query's result and create a dynamic query for another task.
Let's assume my row would be similar to
<agate.Row: ('product_available', Decimal('0.6'), 'Positive')>
<agate.Row: ('product_quality', Decimal('0.5'), 'Negative')>
I intend to generate an array selection for downstream queries which have my_udf that will take arguments from my seeding query's result. For example,
my_udf("product_available", 0.6, 'Positive')
my_udf("product_quality", 0.5, 'Negative')
The problem that I have is some arguments are actual column names, while others are values. Hence, column names should have double quotes, while value must have single quote. For instance, "product_available" vs 'Positive'
My code is
{% macro generate_list_select_from(seeding_query) %}
{# ... other code to execute my seeding query ... #}
{# loop query goes here #}
{% for i in results_list %}
{% set item = "my_udf( {{ i[0] }} , {{ i[1] }}, '{{ i[2] }}' )" %}
{{items.append(item)}}
{% endfor %}
{{ return(items) }}
{% endmacro %}
Below is output when I use my macro
select foo_column,
my_udf( {{ i[0] }} , {{ i[1] }}, '{{ i[2] }}' ),
my_udf( {{ i[0] }} , {{ i[1] }}, '{{ i[2] }}' )
from foo_table
My question is how to create a such string?
Update:
Tried other way, {% set item = "my_udf(" + {{i[0]}} + ")" %}, I end up with a compilation error expected token ':', got '}'
Don't nest your curlies.
~ is the string concatenation operator in jinja. You could use that to build up your function args, including the single quotes:
{% set item = "my_udf(" ~ i[0] ~ ", " ~ i[1] ~ ", '" ~ i[2] ~ "' )" %}
This is pretty hard to read, though. I'd probably add a macro called quoted:
{% macro quoted(s) %}
'{{ s }}'
{% endmacro %}
And then use the join filter to concatenate the items of a list:
{% set args = [i[0], i[1], quoted(i[2])] %}
{% set item = "my_udf(" ~ args | join(", ") ~ ")" %}
Lastly, if the purpose of this macro is to template SQL, then you don't need to use return(items). You should just have the body of the macro template the string you want (see the quoted macro above). That dramatically simplifies things:
{% macro quoted(s) %}
'{{ s }}'
{% endmacro %}
{% macro generate_list_select_from(seeding_query) %}
{# ... other code to execute my seeding query ... #}
{# loop query goes here #}
{% for i in results_list %}
{% set args = [i[0], i[1], quoted(i[2])] %}
my_udf({{ args | join(", "}}){% if not loop.last %},{% endif %}
{% endfor %}
{% endmacro %}

How to create or define a dictionary in liquid templates?

According to shopify,
The only possible types of objects that be created are:
String
Number
Boolean
Nil
Array
EmptyDrop
Any workarounds that anyone might be aware of?
Duplicating a workaround from github, (For more visibility)
Emulating / Fake dictionary workaround
Example:
Declaring:
{% assign rgbColors = "red:#FF0000,green:#00FF00,blue:#0000FF" | split: "," %}
{% assign value = "green" %}
Acessing:
{% for color in rgbColors %}
{% assign colorKeyVal = color | split ":" %}
{% assign colorKey = colorKeyVal[0] %}
{% assign colorValue = colorKeyVal[1] %}
{% if colorKey == value %}
<div style="background: {{ colorValue }}"></div>
{% endif %}
{% endfor %}

Grouping if statements

I'm trying to find a way to have the "if contains" conditions all appear in one line rather than having to repeat the code every single time.
Here is what the code looks like:
var selector = document.querySelector('tr[data-variant-id="{{ item.variant.id }}"] .product__description__variant');
{% assign pre_order_message = '' %}
{% for tag in item.product.tags %}
{% if tag contains 'weeks' %}
{% assign count = count | plus: 1 %}
{% assign pre_order_message = tag | split:'**' | last %}
var para = document.createElement("p");
para.classList.add('hc-shipping');
var node = document.createTextNode("Expected to begin shipping {{ pre_order_message }} from order date.");
para.appendChild(node);
selector.after(para);
{% endif %}
{% endfor %}
var selector = document.querySelector('tr[data-variant-id="{{ item.variant.id }}"] .product__description__variant');
{% assign pre_order_message = '' %}
{% for tag in item.product.tags %}
{% if tag contains 'January' %}
{% assign count = count | plus: 1 %}
{% assign pre_order_message = tag | split:'**' | last %}
var para = document.createElement("p");
para.classList.add('hc-shipping');
var node = document.createTextNode("Expected to begin shipping {{ pre_order_message }}.");
para.appendChild(node);
selector.after(para);
{% endif %}
{% endfor %}
I just want to make sure I can group together what lies between the ' ... ' on the line, that states {% if tag contains ' ' %}. Any way to fix? Thanks!
You can use and/or to combine conditions, more info about this here.
By the way if you just want to make sure a tag exists in product tags, you can do this:
{% if item.product.tags contains 'weeks' %}
Do something...
{% endif %}
Instead of looping through all the tags, you'll save a couple of lines that way ;)
One last thing, you can DRY the code by using code snippets, here's a quick read about this.

Append string to array and join back into string

I'm using Home Assistant templates, which run on Jinja2 script.
I have a group of entities (states.group.doors) that have attribute battery_level. I want to build an array of entities with battery_level < min_battery level and display as a string separated by commas.
I can't figure out what's wrong with my syntax. Two questions:
Is there just a better way overall to create a list that is filtered for battery_level < min_battery_level rather than building an array like I am?
If not, then there must be something wrong with the way I am building this array. Can someone spot it?
Thanks for the help.
The following code does successfully detect battery_level < 98 and display true if anything meets that criteria, so I'm almost there.
{% set min_battery_level = 98 -%}
{% set ns = namespace(found=false, entities=[]) -%}
{% set entities = [] -%}
{% for entity_id in states.group.doors.attributes.entity_id -%}
{% set parts = entity_id.split('.') -%}
{% if (state_attr(entity_id, 'battery_level') | replace("%","") | int) < min_battery_level -%}
{% set ns.found = true -%}
{% set entities = entities + [entity_id] -%}
{% endif -%}
{% endfor -%}
{{ ns.found }}
{{ entities | join(' ') }}
Welp... kept playing with it and got it working as follows:
{% set min_battery_level = 98 -%}
{% set ns = namespace(found=false, entities = []) -%}
{% for entity_id in states.group.doors.attributes.entity_id -%}
{% set name = state_attr(entity_id, 'friendly_name') | string -%}
{% set battery = state_attr(entity_id, 'battery_level') | replace("%","") | int -%}
{% if (battery) < min_battery_level -%}
{% set ns.found = true -%}
{% set ns.entities = ns.entities + [name+' ('+battery|string+'%)'] -%}
{% endif -%}
{% endfor -%}
{{ ns.found }}
{{ ns.entities | join(', ') }}

jekyll assign concat in a loop?

I would like to organize a page based on the number of pages that pass a filter.
I have tried to append truthy pages to a collection but it doesn't work.
{% assign homepage_posts = [] %}
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | concat: [my_page] %}
{% endif %}
{% endfor %}
<h1>size{{homepage_posts.size}}</h1>
<h1>{{homepage_posts}}</h1>
This is not working. Does concat only work with strings?
Jekyll will use Liquid 4 soon. But, for now, no concat.
In your case you can :
Create an empty array (bracket notation doesn't work in liquid) : {% assign homepage_posts = "" | split:"/" %}
{{ homepage_posts | inspect }} --> output : []
And push elements in it :
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | push: mypage %}
{% endif %}
{% endfor %}
{{ homepage_posts | inspect }}
concat filter only works with arrays and will be available in Jekyll when it upgrades to Liquid 4.*:
concat
Concatenates (combines) an array with another array. The resulting
array contains all the elements of the original arrays. concat will
not remove duplicate entries from the concatenated array unless you
also use the uniq filter.
To filter pages containing a specific attribute (in this case homepage: true) you can use a where filter.
Having a page with front matter:
---
homepage: true
---
Then you can have the pages with the homepage: true attribute like:
{% assign homepages = site.pages | where:"homepage","true" %}