Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sort a hash converted to an array in Liquid

Tags:

jekyll

liquid

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?

like image 884
Dan Gravell Avatar asked Jun 17 '11 14:06

Dan Gravell


4 Answers

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 %}
like image 25
Chris Avatar answered Oct 09 '22 20:10

Chris


You could save yourself some trouble and extend Liquid:

  • You can create your own tag blocks, and
  • You can create your own filters

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 :).

like image 86
dnozay Avatar answered Oct 09 '22 22:10

dnozay


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 %}
like image 34
dseif Avatar answered Oct 09 '22 21:10

dseif


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 %}
like image 20
Vanni Totaro Avatar answered Oct 09 '22 21:10

Vanni Totaro