Joining array of objects using liquid template language - json

I'm building a liquid template to help convert some XML to JSON.
Sample XML input:
<ticket>
<account-id type="integer">123456</account-id>
<cc-email>
<cc-emails type="array">
<cc-email>abc#email.com</cc-email>
<cc-email>xyz#email.com</cc-email>
</cc-emails>
<fwd-emails type="array">
<fwd-email>abc#email.com</fwd-email>
<fwd-email>xyz#email.com</fwd-email>
</fwd-emails>
</cc-email>
</ticket>
Desired JSON output:
{
"account-id":"123456",
"cc-email":"abc#email.com,xyz#email.com",
"fwd-email":"abc#email.com,xyz#email.com"
}
Liquid template attempt 1:
{
"account-id":"{{ ticket.account-id }}",
{% assign list = '' | split: ',' %}
{% for item in ticket.cc-email.cc-emails %}
{% assign list = list | push: item %}
{% endfor %}
"cc-email":"{{ list | join: ',' }}",
{% assign list = '' | split: ',' %}
{% for item in ticket.cc-email.fwd-emails %}
{% assign list = list | push: item %}
{% endfor %}
"fwd-email":"{{ list | join: ',' }}"
}
Liquid template attempt 2:
{
"account-id":"{{ ticket.account-id }}",
{% assign list = '' | split: ',' %}
{% for item in ticket.cc-email.cc-emails %}
{% assign list = list | push: item.cc-email %}
{% endfor %}
"cc-email":"{{ list | join: ',' }}",
{% assign list = '' | split: ',' %}
{% for item in ticket.cc-email.fwd-emails %}
{% assign list = list | push: item.fwd-email %}
{% endfor %}
"fwd-email":"{{ list | join: ',' }}"
}
I've also tried appending the items to a string. No matter the method, I only get the following output:
{
"account-id":"123456",
"cc-email":"",
"fwd-email":""
}
Can anyone help point out the issue? Seems like it has to be something simple but I haven't been able to find it.
Many thanks.

Ended up going back to appending strings. Got this to work:
{% assign cc-email-list = '' %}
{% for item in ticket.cc-email.cc-emails %}
{% assign cc-email-list = cc-email-list | Append: item %}
{% if forloop.last == false %}
{% assign cc-email-list = cc-email-list | Append: ',' %}
{% endif %}
{% endfor %}
"cc-email":"{{ cc-email-list }}",

Related

HTML Script For tags is creating extra space

I've been trying to implement an automatic tag with an app for all of my products on Shopify. It works so that the tag created pulls information from the product desciption. If the description states "Pre-order. Ships in 2-3 weeks." or "Pre-order. Ships January 2021." It will pull whatever appears after the the word "Ships in" or Ships". As a result, the tags appear as such:
preorder**2-3 weeks
preorder**January 2021
The tag is used to insert estimated shipping times throughout the site.
The issue right now is that some of the tags have a space after "preorder** " and we need to make sure there is never a space. Any ideas on how to fix? The Body HTML that it is based off is if it contains "Pre-order. Ships in". Here's our current code:
{% assign body = product.body_html | strip_html | newline_to_br %}
{% assign keywords = "Pre-order. Ships" %}
b{% assign tags = '' %}
{% if body contains keywords %}
{% assign lines = body | split: "<br />" %}
{% for line in lines %}
{% if line contains keywords %}
{% assign tokens = line | split: keywords %}
{% if tokens.size == 2 %}
{% assign inner_tokens = tokens[1] | split: "." %}
{% if inner_tokens.size >= 1 %}
{% assign value = inner_tokens[0] | replace: "in", "" | escape_once | replace: " ", "" %}
{% assign tag = "preorder**" | append:value | replace_first: " ", "" %}
{% assign tags = tags | append: tag | append: ',' %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{{ tags }}
Issue
The issue is happening because "Ships in " has one more space than "Ships ".
Solution
You need to remove the space after the "in".
You can do this easily by modifying your replace operator
Code
replace: "in", "" becomes replace: "in ", "" (note the space).
Your code becomes:
{% assign body = product.body_html | strip_html | newline_to_br %}
{% assign keywords = "Pre-order. Ships" %}
b{% assign tags = '' %}
{% if body contains keywords %}
{% assign lines = body | split: "<br />" %}
{% for line in lines %}
{% if line contains keywords %}
{% assign tokens = line | split: keywords %}
{% if tokens.size == 2 %}
{% assign inner_tokens = tokens[1] | split: "." %}
{% if inner_tokens.size >= 1 %}
{% assign value = inner_tokens[0] | replace: "in ", "" | escape_once | replace: " ", "" %}
{% assign tag = "preorder**" | append:value | replace_first: " ", "" %}
{% assign tags = tags | append: tag | append: ',' %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{{ tags }}

jekyll: Is it possible to use a page.variable, as an operator inside a conditional if statement?

JSON file in path: _data/integers.json which looks like this:
{
"100": [
{
"value": "true"
}
]
}
In Jekyll page:
---
integers:
- 100
- 200
---
What I'm trying to do:
{% assign json = site.data.integers %}
{% for integer in page.integers %} // loop
{% if json.{{ integer }}[0].value == "true" %} ... {% endif %}
{% endfor %}
e.g. use the {{ integer }} (aka page.integer[0]) as an operator inside the conditional statement.
Is there a method? ... asking for a friend.
If we keep your json and page.integers as is :
{% assign json = site.data.integers %}
{{ json | inspect }}
{% for integer in page.integers %}
{% comment %} Here we cast our integer to a string,
as json keys are strings
(100 | append:"" => "100")
{% endcomment %}
{% assign intAsStr = integer | append:"" %}
{% comment %} as a possible json[intAsStr] returns an array,
we retrieve the first and only element in it
{% endcomment %}
{% assign data = json[intAsStr].first %}
{% if data["value"] == "true" %}
<h1>We have a match on {{ intAsStr }}</h1>
{% endif %}
{% endfor %}
We can simplify a little with some refactoring
data/integers.json
{
"100": { "value": true }
}
jekyll page
---
integers:
- "100"
- "200"
---
{% assign json = site.data.integers %}
{{ json | inspect }}
{% for integer in page.integers %}
{% assign data = json[integer] %}
{% if data["value"] == true %}
<h1>We have a match on {{ integer }}</h1>
{% endif %}
{% endfor %}

One variable for different collections in Jekyll to use in forloop

I have several collections on my Jekyll site. I've added post navigation to one of the collections displaying a counter on each post page:
{% assign testimonials = site.testimonials %}
{% assign page_order = 1 %}
{% for node in testimonials reversed %}
{% if node.url == page.url %}
{{ page_order }} from {{ forloop.length }}
{% else %}
{% assign page_order = page_order | plus: 1 %}
{% endif %}
{% endfor %}
I would like to make this code work not only for site.testimonials, but for other collections as well. I tried to pass a variable for collections like this:
{% capture label %}{{ page.collection }}{% endcapture %}
{% assign collection = site.collections | where: "label",label | first %}
{% for node in collection reversed %}
{% if node.url == page.url %}
{{ page_order }} from {{ forloop.length }}
{% else %}
{% assign page_order = page_order | plus: 1 %}
{% endif %}
{% endfor %}
But it doesn't work. Is there any way to pass a variable for all collections in Jekyll to use in forloop in post navigation?
When you access collection with site.testimonials, you get collection's documents array.
{{ site.testimonials | inspect }}
# output >> [#<Jekyll::Document ...>, #<Jekyll::Document ...>, ...]
When you access a collection while looping over site.collection, you receive the collection's object :
{% assign collection = site.collections | where: "label",page.collection | first %}
{{ collection | inspect }}
# output >> { "output": true, "label": "collectionLabel",
"docs": [ doc1, docs2, ... ], "files": [],
"directory": "/path/to/collection",
"relative_directory": "_colectionDirectory" }
In your case, you just have to replace :
{% for node in collection reversed %}
By :
{% for node in collection.docs reversed %}

Listing Jekyll Collection pages by tags

I am trying to make a list of pages within a collection grouped by tags. I know this is easily possible with Jekyll's built-in site.tags feature, which I can (and have) achieve with something like:
<h3>Tags</h3>
{% for tag in site.tags %}
{% assign t = tag | first %}
{% assign posts = tag | last %}
<h4><a name="{{t | downcase | replace:" ","-" }}"></a><a class="internal" href="/tagged/#{{t | downcase | replace:" ","-" }}">{{ t | downcase }}</a></h4>
<ul>
{% for post in posts %}
{% if post.tags contains t %}
<li>
{{ post.title }}
<span class="date">{{ post.date | date: "%B %-d, %Y" }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
<hr>
{% endfor %}
In order to get this:
I want to replicate the site.tags function in a Collection called note. Tags in my collections are grouped like they are for posts, using, e.g., tags: [urban growth, California, industrialization] in the YAML header. But I want to get this working with a Collection instead. I can almost achieve what I want with the following:
{% assign collection = site.note | group_by: "tags" | uniq %}
{% for group in collection %}
{% comment %} This is super hacky and I don't like it {% endcomment%}
<h3>{{ group.name | replace: '"', '' | replace: '[', '' | replace: ']', '' }}</h3>
<ul>
{% for item in group.items %}
<li>{{ item.title }}</li>
{% endfor %}
</ul>
{%endfor%}
But as you can probably see, this doesn't break tags out into their unique groups; instead, each set of tags in the YAML is treated as a single, large tag. Any pointers on constructing the array of unique tags and listing the Collection pages under them?
Try this :
{% assign tags = site.note | map: 'tags' | join: ',' | split: ',' | uniq %}
{% for tag in tags %}
<h3>{{ tag }}</h3>
<ul>
{% for note in site.note %}
{% if note.tags contains tag %}
<li>{{ note.title }}</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
The tags assignment method mentioned in David Jacquel's answer can be simplified as:
{% assign tags = site.note | map: 'tags' | uniq %}

Downcase a liquid object

i want to downcase Liquid-object in Jekyll:
{{ page.tags | downcase }}
{{ page.tags | downcase }} doesn't work. Do you have an idea?
Thanks!
If you want to print a list of downcased page's tags, you can do :
{% comment %}Create an empty array{% endcomment %}
{% assign tagArray = "" | split: "/" %}
{% for tag in page.tags %}
{% assign tagDowncased = tag | downcase %}
{% assign tagArray = tagArray | push: tagDowncased %}
{% endfor %}
<p>{{ tagArray | joint: ", " }}</p>