Can't filter tags by another variable

Hi, friends

So, I’m new to jekyll and programming stuff. I’m building a blog in 3 languages, with a variable ‘lang’ in every post that identifies it (br, en or es).

I have a piece of code that shows all the tags (in all languages, and with the number of posts of each category in superscript), and by clicking them, it lists the posts in that tag. That’s the original code:

{%- assign _tag_max_size = 1 -%}
{%- assign _tag_min_size = 1 -%}
{%- assign _tag_cur_size = 1 -%}
{%- assign _tags = site.tags -%}
{%- for _tag in _tags | sort -%}
  {%- assign _tag_cur_size = _tag | size -%}
  {%- if _tag_cur_size > _tag_max_size -%}
    {%- assign _tag_max_size =  _tag_cur_size -%}
  {%- endif -%}
  {%- if _tag_cur_size < _tag_min_size -%}
    {%- assign _tag_min_size = _tag_cur_size -%}
  {%- endif -%}
{%- endfor -%}
{%- assign _tag_gap_size =  _tag_max_size | minus: _tag_min_size | plus: 1 | divided_by: 4 -%}
{%- if _tag_gap_size < 1 -%}
  {%- assign _tag_gap_size = 1 -%}
{%- endif -%}
<div class="site-tags js-tags">
  <ul class="menu">
    {%- assign poste = site.posts | where: 'lang', page.lang -%}
    {%- if page.lang == 'br' -%}
    <li>
      <button type="button" class="button button--secondary button--pill tag-button tag-button--all" data-encode="">
        Mostrar todos<div class="tag-button__count">{{ poste | size }}</div>
      </button>
    </li>
    {%- elsif page.lang == 'en' -%}
      <li>
        <button type="button" class="button button--secondary button--pill tag-button tag-button--all" data-encode="">
          Show all<div class="tag-button__count">{{ poste | size }}</div>
        </button>
      </li>
    {%- else -%}
      <li>
        <button type="button" class="button button--secondary button--pill tag-button tag-button--all" data-encode="">
          Mostrar todos<div class="tag-button__count">{{ poste | size }}</div>
        </button>
      </li>
    {%- endif -%}
    {%- for _tag in _tags -%}
      {%- assign _tag_cur_size = _tag | size -%}
      {%- assign _tag_min_1 = _tag_min_size -%}
      {%- assign _tag_max_1 = _tag_min_1 | plus: _tag_gap_size -%}
      {%- assign _tag_min_2 = _tag_max_1 -%}
      {%- assign _tag_max_2 = _tag_min_2 | plus: _tag_gap_size -%}
      {%- assign _tag_min_3 = _tag_max_2 -%}
      {%- assign _tag_max_3 = _tag_min_3 | plus: _tag_gap_size -%}
      {%- assign _tag_min_4 = _tag_max_3 -%}
      {%- assign _tag_max_4 = _tag_min_4 | plus: _tag_gap_size -%}
      {%- if _tag_cur_size >= _tag_min_1 and _tag_cur_size < _tag_max_1 -%}
        {%- assign _c_index = 1 -%}
      {%- elsif _tag_cur_size >= _tag_min_2 and _tag_cur_size < _tag_max_2 -%}
        {%- assign _c_index = 2 -%}
      {%- elsif _tag_cur_size >= _tag_min_3 and _tag_cur_size < _tag_max_3 -%}
        {%- assign _c_index = 3 -%}
      {%- elsif _tag_cur_size >= _tag_min_4 and _tag_cur_size < _tag_max_4 -%}
        {%- assign _c_index = 4 -%}
      {%- else -%}
        {%- assign _c_index = 4 -%}
      {%- endif -%}
      <li><button type="button" class="button button--pill tag-button tag-button-{{ _c_index }}" data-encode="{{ _tag[0] | strip | url_encode }}">
          <span> {{ _tag[0] }} </span><div class="tag-button__count">{{ _tag[1].size }}</div>
        </button>
        {%- endfor -%}
      </li>
  </ul>
</div>

I’d like to filter the tags, to show only the ones in the user’s language. I’ve tried to change the 4th line to this:

{%- assign _tags = site.posts | where: 'lang', page.lang | map: 'tags' | uniq | sort -%}

But it doesn’t recognize apropriately the ‘uniq’ filter, not listing the posts correctly. If I do the same without ‘uniq’, it generates correctly (only the tags in the correct language, and generating the respective posts), but with duplicated tags (one tag per post).

That’s the website blog listing page:

https://gustavosabbag.github.io/en/articles.html

And this is the repository, directly to the html file that’s generating the tags:

https://github.com/gustavosabbag/gustavosabbag.github.io/blob/master/_includes/tags.html

Can someone help me, please?

So without uniq you get duplicate items. What happens when you do you uniq? Are the results the same as before?

See uniq here

See if you can get that example to work…

Try leaving the sort part out

You can try adding sort before uniq . It is more efficient. and in Bash unique only sorted results though that may not matter here.

1 Like

When I use uniq, it shows the correct tags (only the ones in the correct language), but when I click them, it doesn’t list the posts. When I use the same code without uniq it generates the duplicated tags, but listing correctly the posts per tag.

I’ve tried sorting before or leaving it out, the results are the same… It seems that the problem is with uniq filter, since it recognizes appropriately the other filters. I can’t understand why the tag doesn’t list the posts if I filter with uniq.

A solution would be using another method to filter the duplicated tags, but I don’t know any…

Could you provide links to your live site and repository (if they are publicly accessible)?

1 Like

Sure

I’ve created some example posts and tags to be clear. That’s the website blog listing page:

https://gustavosabbag.github.io/en/articles.html

And this is the repository, directly to the html file that’s generating the tags:

https://github.com/gustavosabbag/gustavosabbag.github.io/blob/master/_includes/tags.html

Please note: The code in the file is the base one, without the filters and showing all the tags (including the other language tags - the line {%- assign _tags = site.tags -%} ). However, the listing only shows the articles that have the same language as the current page (i.e., the listing is correct). Hope I made myself clear, I can try to explain better if needed.

Thank you for your help.

Have you tried building the site locally?
I’m getting numerous syntax-error warnings from Liquid especially regarding
site.categories.['Entendendo Política'].

Notes:

  • Liquid parses text lexically. So even if you enclose Liquid within HTML comments, it gets parsed. For example, <!-- {% foo %} --> will raise a Liquid Error and abort build even if it is commented out.
  • Either use dot-notation or bracket-notation.
    site.categories.["foobar"] is wrong usage. The correct syntax is either site.categories.foobar or site.categories["foobar"] (Note the lack of a period prior to the opening square bracket).
1 Like

Sure, I’m testing locally, but I’m running it with the following command: LC_ALL="en_US.UTF-8" bundle exec jekyll serve

site.categories.['Entendendo Política'] is just some test posts I made but didn’t deleted it yet, because the website is not complete.

Thank you for the helpfull notes. These errors is due to the fact that I edited a theme, and I’m still learning how to do that apropriately.

I think i see the problem here.

When you get

site.tags

You end up with Jekyll tag objects.

When you use

site.posts

You get post objects.

When you use

site.posts | map: tags

You get the tags key on each post. Which is an array of strings.

For example, on my blog I’ve run this. I don’t have .lang but you could put the where filter here.

{%- assign tags = site.posts | map: "tags" -%}

{{ tags | inspect }}

[["learning"], ["links", "learning"], ["rust", "go"], 
["learning"], ["python", "machine-learning", "api"]]

If you apply uniq to that, you will not get the unique strings, you’ll get the unique arrays. So you result will mostly be the same.

So… what you want to do is flatten the array so it becomes this:

["learning", "links", "learning", "rust", "go", "learning", "python", "machine-learning", "api"]

And then you apply uniq and sort.

So you’ll get

["api", "go", "learning", "machine-learning", "python", "rust", ]

Here is a solution.

1. Setup tags

As before. You can add | where: 'lang', page.lang as needed.

{%- assign tags = site.posts | map: "tags" -%}

2. Flatten

There is no flatten filter in Jekyll - there is a builtin way for JS and Python though.

So I create an empty array.

{% assign my_array = "" | split: "|" %}

I had to use a hack to split a string, since you can’t create a variable as {} - it will just be nil.

Because my data is nested, I need two for loops. One for the outer array and one for the inner array of usually 1 or 2 or sometimes 3 items.

{% for tag_list in tags %}
    {% for tag in tag_list %}
        {% assign my_array =  my_array | push: tag %}
    {% endfor %}
{% endfor %}

If you are used to hashes, that is the same as:

x = {}
x["a"] = 1
x["b"] = 2

Except I push (append) to the array and then overwrite the initial array.

Result is flat!

{{my_array | inspect }}
["learning", "links", "learning", "rust", "go", "make", "reflection", "motivation", "reflection", "art", "music", "links", "future", "predictions", "javascript", "html", "tdd", "tests", "links", "projects", "static-site-generator", "links", "interviews", "learning", "inspiration", "motivation", "motivation", "projects", "inspiration", "motivation", "reflection", "cms", "static-site-generator", "tools", "interviews", "javascript", "static-site-generator", "future", "predictions", "cms", "security", "python", "learning", "python", "machine-learning", "api"]

3. Remove dups!

{% assign my_array = my_array | uniq | sort %}

Check the output.

{{ my_array  | inspect }} 
["api", "art", "cms", "future", "go", "html", "inspiration", "interviews", 
"javascript", "learning", "links", "machine-learning", "make", "motivation", 
"music", "predictions", "projects", "python", "reflection", "rust", "security", "static-site-generator", "tdd", "tests", "tools"]

4. Lookup tag

You don’t have to do this step but it demonstrates how the next one will work.

If I use site.tags, I can lookup the posts in a given tag. Here I hardcoded it and get back and array or posts.

{{ site.tags['api'] }}
[
  #<Jekyll::Document _posts/2018-02-20-tensorflow-on-a-web-api.md collection=posts>, 
  #<Jekyll::Document...,
  #<Jekyll::Document ...,
]

5. Show posts

Here I iterate through those tag strings, printing the tag name and then also getting all the posts in that tag and showing their titles.

{% for tag_str in my_array %}

{% assign posts_in_tag = site.tags[tag_str] %}
- {{ tag_str }} ({{ posts_in_tag.size }}) - posts: {{ posts_in_tag | map: 'title' }}

{% endfor %}

Result:

  • api (1) - posts: TensorFlow on a Web API
  • art (1) - posts: The Art of Code
  • cms (2) - posts: Static Site CMS toolsThe future of websites - headless CMS
  • future (2) - posts: Predicting the future of web developmentThe future is bright for Java
  • go (1) - posts: Use a package manager - or not?
  • links (4) - posts: ResourcesLinks - MayLinks - FebLinks

I hope that helps! :slight_smile:

2 Likes

Thank you very much!!! I was haunted by this for over a month… It finally worked!!!

1 Like

You’re welcome.

In Jekyll, use inspect more often for debugging. Then you can see something is an array for example, when leaving out inspect can be confusing.

E.g. given my_var set as
[ "a", "b c", "d"]

This would render exactly as above with quotes and brackets, for {{ my_var | inspect }}

But Jekyll would represent it as a b c d if you just did {{ my_var }}

1 Like

Thanks for the kindly suggestion. I’ll certainly start using it!

1 Like