Display a menu with distinct categories using Jekyll - jekyll

I would like to show a menu that displays only the distinct categories.
Imagine to have the following structure:
_folder1
com1.html
com2.html
com3.html
Now, let me focus only about three files.
For every file contained in the _folder1 you have the following YAML MATTER
TITLE: 1File
type: y
project: 1
TITLE: 2File
type: y
project: 1
TITLE: 3File
type: y
project: 2
Now, I'd like to show the following list:
Projects
1
2
and I don't want to have a double 1.
What is the best practice to reach it in Jekyll ?

It's possible, but you need some very ugly string manipulation hacks to achieve it.
As far as I know, there's no proper way in Liquid to create arrays by yourself.
So 90% of the following solution consists of abusing strings in order to create arrays.
<!-- Step 1: create an array with all projects (with duplicates) -->
{% for page in site.pages %}
{% if page.project %}
{% capture tmp %}{{ tmp }}#{{ page.project }}{% endcapture %}
{% endif %}
{% endfor %}
{% assign allprojects = tmp | remove_first: '#' | split: '#' | sort %}
<!-- Step 2: create an array of unique projects (without duplicates) -->
{% for project in allprojects %}
{% unless tmp2 contains project %}
{% capture tmp2 %}{{ tmp2 }}#{{ project | strip }}{% endcapture %}
{% endunless %}
{% endfor %}
{% assign uniqueprojects = tmp2 | remove_first: '#' | split: '#' | sort %}
<!-- Step 3: display unique projects -->
<h1>Projects:</h1>
<ul>
{% for project in uniqueprojects %}
<li>{{project}}</li>
{% endfor %}
</ul>
In the end, step 3 will generate the following HTML...exactly as requested:
<h1>Projects:</h1>
<ul>
<li>1</li>
<li>2</li>
</ul>

Related

Loop unique values in nested data tree in Jekyll

I have a data files with different items. Each item have nested tasks. I am trying to loop the nested tasks and present each task by the task type.
YML DATA
- name: Outside
description: Description
tasks:
- type: Food
name: Eat it outside
status: working
- type: Drinks
name: Drink it outside
status: working
- name: Inside
description: Description
tasks:
- type: Food
name: Eat it inside
status: pending
- type: Drinks
name: Drink it inside
status: working
Liquid
{% for item in site.data.info %}
{% assign grouped-tasks-by-type = item.tasks | group_by: "type" %}
{% for task in grouped-tasks-by-type %}
<h2 class="task-type">{{ task.type }}</h2>
<ul>
{% for task in item.tasks %}
{% if task.status == 'working' %}
<li>{{ item.name }}: {{ task.name }}</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
{% endfor %}
Expected result (HTML)
<h2 class="task-type">Food</h2>
<ul>
<li>Outside: Eat it outside<li>
</ul>
<h2 class="task-type">Drinks</h2>
<ul>
<li>Outside: Drink it outside<li>
<li>Inside: Drink it inside<li>
</ul>
However, I am getting a full blank result. Is this possible to do with group_by?
I hope my algorithm below serves you well. I was able to achieve your expect end result with the algorithm. I was unable to utilize the group_by in my solution. The sample code contains Liquid comment blocks to explain my thought process.
Testing
I used the minima git repo, with command jekyll s. I placed your YML data in a file with path _data/info.yml in my locally cloned minima git repo. I used post.html as the code sandbox.
You can print out the Liquid variables to the DOM by doing {{ all_food_types | json }} in the code.
Solution
{%- comment -%}
End Goal: Find all the food types for <h2>.
1. Use map: "tasks" to gather all the tasks into a single array.
This will cause the loss of Outside/Inside information for each task
2. Use map: "type" to create a list of food types ("Food", "Drinks")
3. Use uniq to remove duplicate food types from the array
{%- endcomment -%}
{% assign all_food_types = site.data.info | map: "tasks" | map: "type" | uniq %}
{%- comment -%}
End Goal: Loop through all the data looking
for a specific food type (Food, Drinks)
and group them together
{%- endcomment -%}
{% for food_type in all_food_types %}
<h2 class="task-type">{{ food_type }}</h2>
<ul>
{% for item in site.data.info %}
{% for task in item.tasks %}
{% if task.status == 'working' and task.type == food_type %}
<li>{{ item.name }}: {{ task.name }}</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
{% endfor %}
An aside, for whatever reason, my liquid ends up using a lot of arrays and for-loops.

Comparing items in different data files with shared identifier in Liquid/Jekll

I'm building a Jekyll site that has a page that displays product information from a JSON file. I was wondering if it would be possible to filter what products are displayed by the corresponding quantity listed in another CSV file with stock numbers. Both of these files use the same identifier for each product. E.G. something along the lines of:
{% for product in site.data.products %}
{% if product.identifier == stock.identifier and current-stock < X %}
Display the product
{% endif %}
{% endfor %}
You can use the where filter :
<ul>
{% for product in site.data.products %}
<li>
{% assign id = product.identifier %}
{% assign data = site.data.stock | where: "identifier", id | first %}
{% if data %}
{{ data | inspect }}
{% else %}
No data available
{% endif %}
</li>
{% endfor %}
</ul>

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

Include a file, but only if it exists?

I'm creating a style guide in Jekyll and using Collections to define different elements of the guide. For example, headings, lists, etc.
I'm trying to separate the Sass into files that match up with the partials, one to one, and I'd like to render the Sass files as part of each collection.
So, something like:
{% if _includes/_sass/{{ entry.title | append: ".scss"}} %}
{% highlight sass %}
{% include _includes/_sass/{{ entry.title | append: ".scss" }} %}
{% endhighlight %}
{% endif %}
Basically, what I want is "Include a file in this directory that has the same name as this entry in my collection. If it doesn't exist, don't break."
How do I do this? I've explored storing the file path in a variable but can't seem to get that to work.
Thanks in advance.
It can be done.
This works on Jekyll 3 but it can certainly be ported to Jekyll 2.
Starting from a base install (jekyll new)
_config.yml
collections:
guide:
sasssamples:
Style guide files
Our samples will be grouped in the _guide collection.
Example file : _guide/header/header1.hmtl
---
title: Header level 1
---
<h1>Header level 1</h1>
SCSS samples
We want our SCSS samples to be included in our css/main.scss and use variables defined in our other SCSS files. Our samples will be integrated at the end of our css/main.scss
We don't want our SCSS samples to render as css so no .scss extension. Switch to .txt extension
We want to access SCSS samples from a list. Let's put them in a sasssamples collection.
Example file : _sasssamples/header/header1.txt
---
---
h1{
color: $brand-color;
border: 1px solid $brand-color;
}
SCSS samples integration
Add this code at the very end of you bootstraping scss file (css/main.scss on a base Jekyll install)
css/main.scss
[ original code ... ]
{% comment %} Selecting a collection the Jekyll 3 way. See https://github.com/jekyll/jekyll/issues/4392 {% endcomment %}
{% assign scssCollection = site.collections | where: 'label', 'sasssamples' | first %}
{% comment %}
Printing documents in sasssamples collection.
All SCSS from style guide are sandboxed in .guide class
This allows us to apply styles only to style guide html samples
{% endcomment %}
.guide{
{% for doc in scssCollection.docs %}
{{ doc.content }}
{% endfor %}
}
The style guide
<h2>Style guide</h2>
{% comment %}Selecting a collection the Jekyll 3 way. See https://github.com/jekyll/jekyll/issues/4392 {% endcomment %}
{% assign guideCollection = site.collections | where: 'label', 'guide' | first %}
{% assign scssCollection = site.collections | where: 'label', 'sasssamples' | first %}
{% comment %} Looping hover style guide samples {% endcomment %}
{% assign samples = guideCollection.docs %}
{% for sample in samples %}
<article>
<h3>Element : {{ sample.title }}</h3>
<h4>Render</h4>
<div class="guide">
{{ sample.content }}
</div>
<h4>html code</h4>
{% highlight html %}{{ sample.content }}{% endhighlight %}
{% comment %}
Changing a path like : _guide/headers/header1.html
to : _sasssamples/headers/header1.txt
{% endcomment %}
{% assign scssPath = sample.path | replace: '_guide', '_sasssamples' %}
{% assign scssPath = scssPath | replace: '.html', '.txt' %}
{% comment %} Try to find a SCSS sample with equivalent path {% endcomment %}
{% assign scssSample = scssCollection.docs | where: 'path', scssPath | first %}
{% comment %}We print SCSS sample only if we found an equivalent path{% endcomment %}
{% if scssSample != nil %}
<h4>SCSS code</h4>
{% highlight css %}{{ scssSample.content }}{% endhighlight %}
{% endif %}
</article>
{% endfor %}
Done!
Seems it only miss on assigning the correct path
{% if _includes/_sass/{{ entry.title | append: ".scss"}}
Need to be replaced to relative path to the scss file:
{% assign scssPath = 'relative/path/to/your/scss/' %}
{% if {{ entry.title | append: ".scss" | prepend: scssPath }} != nil %}

List tags within a specific category in Jekyll

I am migrating my Wordpress blog to Jekyll, which I like a lot so far. The current setup in the new site is like this:
use category to distinguish two types of posts (e.g., blog and portfolio)
use tag as normal
The challenge right now is to display all tags within a category because I want to create two separate tag clouds for two types of posts.
As far as I know, Liquid supports looping over all tags in a site like this:
{% for tag in site.tags %}
{{ tag | first }}
{% endfor %}
But I want to limit the scope to a specific category and am wishing to do something like this:
{% for tag in site['category'].tags %}
{{ tag | first }}
{% endfor %}
Any advice will be appreciated.
This seems to work for all kinds of filters like category or other front matter variables - like "type" so I can have type: article or type: video and this seems to get tags from just one of them if I put that in the 'where' part.
{% assign sorted_tags = site.tags | sort %}
{% for tag in sorted_tags %}
{% assign zz = tag[1] | where: "category", "Photoshop" | sort %}
{% if zz != empty %}
<li><span class="tag">{{ tag[0] }}</span>
<ul>
{% for p in zz %}
<li>{{ p.title }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
zz is just something to use to filter above the first tag[0] since all it seems to have is the tag itself, so you can filter anything else with it. tag[1] has all of the other stuff.
Initially I was using if zz != null or if zz != "" but neither of them worked.
This will work, it will list only the tags on post of category 'X'. Replace X with the name of the category.
{% for post in site.categories['X'] %}
{% for tag in post.tags %}
{{ tag }}
{% endfor %}
{% endfor %}