JINJA/dbt macro unneeded whitespace? - sql-server

I am trying to develop a dbt macro using jinja-sql to create a template for my base hub models. How do I get my hardcoded fields directly underneath my generated code? I have fooled around with all of the -'s trying to limit the whitespace to no avail.
{{hub_table}} AS (
SELECT
{%- if not leading_commas -%}
{%- for column in column_names %}
{% if column not in hub_default_columns %}{{column | lower~","}}{%- endif -%}
{%- endfor -%}
{%- endif -%}
dvloaddatetime AS dvLoadDateTime,
dvlastseendate AS dvLastSeenDate,
dvsourceid AS dvSourceID
FROM source
)
SELECT *
FROM {{hub_table}}
{% endset %}
{% if execute %}
{{ log(hub_model_sql, info=True) }}
{% do return(hub_model_sql) %}
{% endif %}
{% endmacro %}
My output that I receive is shown below.
WITH source AS (
SELECT *
FROM {{ source('-----', 'h_CLIENT') }}
),
h_CLIENT AS (
SELECT
h_client_hashkey,
clientsid,
dvloaddatetime AS dvLoadDateTime,
dvlastseendate AS dvLastSeenDate,
dvsourceid AS dvSourceID
FROM source
)
SELECT *
FROM h_CLIENT
The output I am wanting/hoping to receive is shown below.
WITH source AS (
SELECT *
FROM {{ source('-----', 'h_CLIENT') }}
),
h_CLIENT AS (
SELECT
h_client_hashkey,
clientsid,
dvloaddatetime AS dvLoadDateTime,
dvlastseendate AS dvLastSeenDate,
dvsourceid AS dvSourceID
FROM source
)
SELECT *
FROM h_CLIENT

Whitespace control just impacts the space/newlines between that tag and the tags immediately before or after it. With loops, you need to imagine the contents of the loops repeated; for if statements, the whitespace control of the if block will be applied, even if the contents are empty. If two tags are either ends of whitespace, either tag's - will remove that whitespace.
In your case, you're getting extra newlines when if column not in hub_default_columns evaluates to false. Since there's no whitespace control on the beginning of that tag, you're getting a newline for each loop, even if the contents of the if block are empty.
To fix this, you want to allow whitespace inside the if block, but have the block itself remove surrounding whitespace. Similarly, you need to tweak the first if statement after select to make sure there is a newline after select, even when leading_commas is true.
This:
{%- set column_names = ["a", "b", "c", "d"]-%}
{%- set hub_default_columns = ["c", "d"]-%}
{%- set leading_commas = false %}
SELECT
{% if not leading_commas -%}
{%- for column in column_names -%}
{%- if column not in hub_default_columns -%}
{{column | lower~","}}
{% endif -%}
{%- endfor -%}
{%- endif -%}
dvloaddatetime AS dvLoadDateTime,
dvlastseendate AS dvLastSeenDate,
dvsourceid AS dvSourceID
Compiles to:
SELECT
a,
b,
dvloaddatetime AS dvLoadDateTime,
dvlastseendate AS dvLastSeenDate,
dvsourceid AS dvSourceID

Related

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

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 -%}

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

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