سؤال

My understanding is that Liquid converts Ruby Hashes to arrays for use in tags. For example, when using Jekyll:

{% for category in site.categories %}
    <li>{{ category[0] }}</li>
{% endfor %}

... converts site.categories to an array of tuples in which [0] refers to the key, [1] the list of values.

If I wanted the above category map to be sorted alphabetically by the key ([0] of each tuple) how can I do this?

هل كانت مفيدة؟

المحلول

Neither the default Liquid implementation nor the additions made by Jekyll allow for what you want.

I'm afraid what you want is simply not possible with the current setup. You would have to monkeypatch Jekyll or Liquid in order to make the hashes return their keys in a sorted order.

نصائح أخرى

This is an old question, but I just spend the last little while figuring this out for myself. I used the following code to achieve what you ( and I ) wanted.

{% capture get_items %}
 {% for cat in site.categories %}
   {{ cat | first }}
 {% endfor %}
{% endcapture %}
{% capture num_words %}
 {{ get_items | split:' ' |  sort | join:' ' | number_of_words }}
{% endcapture %}
{% for item in (1..num_words) %}
 <li>{{ get_items | split:' ' |  sort | join:' ' | truncatewords:item | remove:'.    ..' |    split:' ' | last }}</li>
{% endfor %}

You can also use arrays instead of hashes!

Instead of using this yaml:

categories:
  a_category: category description
  another_category: another category description

You can use this one:

categories:
  - {name: 'a category', description: 'category description'}
  - {name: 'another category', description: 'another category description'}

And then you can iterate like this, and order will be preserved :)

{% for category in site.categories %}
  <li>{{ category['name'] }}</li>
{% endfor %}

Index of posts by alphabetically sorted tags (spaces not allowed in tag names):

{% capture tags %}
  {% for tag in site.tags %}
    {{ tag[0] }}
  {% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}

{% for tag in sortedtags %}
  <h4>#{{ tag }}</h4>
  <ul>
  {% for post in site.tags[tag] %}
    <li>
      <span>{{ post.date | date_to_string }}</span>
      <a href="{{ post.url }}">{{ post.title }}</a>
    </li>
  {% endfor %}
  </ul>
{% endfor %}

You can sort by the key using the following method (that is compatible with Github Pages):

{% assign sorted_categories = (site.categories | sort:0) %}
{% for category in sorted_categories %}
   <!-- Using the 'capitalize' filter in Liquid Tags - you can leave this out -->
   <li>{{category[0] | capitalize }}</li>

   {% assign sorted_catposts = (category[1] | sort: 'title', 'last') %}
   {% for catpost in sorted_catposts %}
     <!-- The posts within each category are now alphabetically sorted by title -->
     <!-- Do stuff with each post in each category -->
   {% endfor %}
{% endfor %}

Hope this helps.

I also want to be part of this obfuscated competition (dudes, number_of_words, seriously?).

This code is building the list tag by tag, looking for the next tag in lexicographic order in the list at each step. It's in O(n²) where n is the number of tags.

<section>
  <h1>Pick a tag!</h1>
  <ul id="recent_posts">
    {% assign current_tag = ' ' %}
    {% for t in site.categories %}
      <li class="post">
        {% assign next_tag = 'ZZZ' %}
        {% for item in site.categories %}
          {% assign tag = item.first %}
          {% if tag > current_tag and tag < next_tag %}
            {% assign next_tag = tag %}
          {% endif %}
        {% endfor %}
        {{ next_tag | category_link }} {{ site.categories[next_tag].size }}
        {% assign current_tag = next_tag %}
      </li>
    {% endfor %}
  </ul>
</section>

BTW, Liquid comments look absurdly heavy.

You could save yourself some trouble and extend Liquid:

e.g.

# https://gist.github.com/dnozay/026862f5d65dcb8b4353

module Jekyll
  module Toolbox
    def keys(hash)
      hash.keys
    end
    def to_ul(collection)
      result = ''
      collection.each do |item|
        result << "<li>#{item}</li>"
      end
      result
    end
  end
end

Liquid::Template.register_filter(Jekyll::Toolbox)

Usage:

{{ myhash | keys | to_ul }}

Examples:

# https://gist.github.com/dnozay/026862f5d65dcb8b4353

@context = { 'myhash' => { 'b' => 'B', 'a' => 'A', 'c' => 'C' } }
@template = Liquid::Template.parse("{{ myhash | keys | to_ul }}")
@template.render(@context)  # => "<li>b</li><li>a</li><li>c</li>"
@template = Liquid::Template.parse("{{ myhash | keys | sort | to_ul }}")
@template.render(@context)  # => "<li>a</li><li>b</li><li>c</li>"

If you feel lucky you can look on github for the for.rb file and extend the for syntax to handle hashes better :).

Here's how I fixed Nikos problem:

{% capture get_items %}
   {% for cat in site.categories %}
    {{ cat | first | replace: ' ', '_' }}
   {% endfor %}
{% endcapture %}
{% capture num_words %}
  {{ get_items | split:' ' |  sort | join:' ' | number_of_words }}
{% endcapture %}
{% for item in (1..num_words) %}
  <li>{{ get_items | split:' ' |  sort | join:' ' | truncatewords:item | remove:'...' | split:' '  | last | replace: '_', ' ' }}</li>
{% endfor %}

Now how to get this working with HAML...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top