Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Quick'n'dirty Jekyll plugin for sorted cycle

Jekyll sorted_for plugin

Quick'n'dirty Jekyll plugin for sorted cycle.

Install

Copy sorted_for.rb to _plugins/ directory of your Jekyll site.

Usage

Instead of for in templates use sorted_for. Add sort_by parameter with property you want to sort by, or leave it out if the array contains primitives (i.e. strings or numbers). Also supports reversed parameter as the original for tag.

{% sorted_for node in site.pages reversed sort_by:weight %}
  {{ node.title }}
{% endsorted_for %}

To use a custom sort property (eg. weight as in example above) add it to YAML Front Matter of your pages - see https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter

String comparisons are case-insensitive by default, whether the collection contains strings or objects sorted by a string property. To enable case-sensitive string comparisons, apply the case_sensitive:true parameter.

To iterate over the keys in a hash, use sorted_keys_for.

{% sorted_keys_for tag in site.tags %}
  <a href="/tags/{{ tag | downcase | replace:" ","-"}}.html">{{ tag }}</a><br />
  Num posts: {{ site.tags[tag].size }}
{% endsorted_keys_for %}

Nested structures

To iterate over a nested array or hash, you will need to assign the inner collection to a variable before iterating over it using sorted_for or sorted_keys_for. This example assumes that the total number of posts per year and per month are stored in two separate (parallel) hashes, where page.month_totals is a hash-of-hashes:

<ul>
  {% sorted_keys_for year in page.year_totals reversed %}
  <li><a href="#{{ year }}">{{ year }}</a> - {{ page.year_totals[year] }} posts
    <ul>
      {% assign month_totals = page.month_totals[year] %}
      {% sorted_keys_for month in month_totals reversed %}
      <li><a href="#{{ year }}-{{month}}">{{ month | int_to_month_name }}</a>
          - {{ month_totals[month] }} posts</li>
      {% endsorted_keys_for %}
    </ul>
  </li>
  {% endsorted_keys_for %}
</ul>

This example assumes that the total number of posts per year and per month are stored in a single hash-of-hashes:

<ul>
  {% sorted_keys_for year in page.totals reversed %}
  {% assign totals = page.totals[year] %}
  <li><a href="#{{ year }}">{{ year }}</a> - {{ totals.year_total }} posts
    <ul>
      {% sorted_keys_for month in totals.month_totals reversed %}
      <li><a href="#{{year}}-{{month}}">{{ month | int_to_month_name }}</a>
          - {{ totals.month_totals[month] }} posts</li>
      {% endsorted_keys_for %}
    </ul>
  </li>
  {% endsorted_keys_for %}
</ul>
require 'rake'
require 'rake/testtask'
task :default => [:test]
Rake::TestTask.new(:test) do |test|
test.libs << 'test'
test.pattern = 'test/*_test.rb'
test.verbose = true
end
module Jekyll
module SortedForImpl
def render(context)
sorted_collection = collection_to_sort context
return if sorted_collection.empty?
sort_attr = @attributes['sort_by']
case_sensitive = @attributes['case_sensitive'] == 'true'
i = sorted_collection.first
if sort_attr != nil
if i.to_liquid[sort_attr].instance_of? String and not case_sensitive
sorted_collection.sort_by! { |i| i.to_liquid[sort_attr].downcase }
else
sorted_collection.sort_by! { |i| i.to_liquid[sort_attr] }
end
else
if i.instance_of? String and not case_sensitive
sorted_collection.sort_by! { |i| i.downcase }
else
sorted_collection.sort!
end
end
original_name = @collection_name
result = nil
context.stack do
sorted_collection_name = "#{@collection_name}_sorted".sub('.', '_')
context[sorted_collection_name] = sorted_collection
@collection_name = sorted_collection_name
result = super
@collection_name = original_name
end
result
end
end
class SortedForTag < Liquid::For
include SortedForImpl
def collection_to_sort(context)
return context[@collection_name].dup
end
def end_tag
'endsorted_for'
end
end
class SortedKeysForTag < Liquid::For
include SortedForImpl
def collection_to_sort(context)
return context[@collection_name].keys
end
def end_tag
'endsorted_keys_for'
end
end
end
Liquid::Template.register_tag('sorted_for', Jekyll::SortedForTag)
Liquid::Template.register_tag('sorted_keys_for', Jekyll::SortedKeysForTag)
require 'liquid'
require 'test/unit'
require File.join(Gem.loaded_specs['liquid'].full_gem_path, 'test', 'test_helper.rb')
require File.join(File.dirname(__FILE__), '..', 'sorted_for.rb')
class SortedForTagAttributesTest < Test::Unit::TestCase
include Liquid
TEMPLATE = '{% sorted_for i in array sort_by:weight %}{{ i.name }}:{{ i.weight }} {% endsorted_for %}'
REVERSED = '{% sorted_for i in array reversed sort_by:weight %}{{ i.name }}:{{ i.weight }} {% endsorted_for %}'
def test_empty
assert_template_result('', TEMPLATE, 'array' => [])
end
def test_single_element
assert_template_result(
'a:2 ',
TEMPLATE,
'array' => [{'name' => 'a', 'weight' => 2}])
end
def test_multiple_elements
assert_template_result(
'c:0 b:1 a:2 ',
TEMPLATE,
'array' => [
{'name' => 'a', 'weight' => 2},
{'name' => 'b', 'weight' => 1},
{'name' => 'c', 'weight' => 0},
])
end
def test_reversed
assert_template_result(
'a:2 b:1 c:0 ',
REVERSED,
'array' => [
{'name' => 'a', 'weight' => 2},
{'name' => 'b', 'weight' => 1},
{'name' => 'c', 'weight' => 0},
])
end
end
class SortedForTagPrimitivesTest < Test::Unit::TestCase
include Liquid
TEMPLATE = '{% sorted_for i in array %}{{ i }} {% endsorted_for %}'
REVERSED = '{% sorted_for i in array reversed %}{{ i }} {% endsorted_for %}'
def test_empty
assert_template_result('', TEMPLATE, 'array' => [])
end
def test_single_element
assert_template_result('a ', TEMPLATE, 'array' => ['a'])
end
def test_multiple_elements
assert_template_result('a b c ', TEMPLATE, 'array' => ['c', 'a', 'b'])
end
def test_reversed
assert_template_result('c b a ', REVERSED, 'array' => ['c', 'a', 'b'])
end
end
class SortedKeysForTagTest < Test::Unit::TestCase
include Liquid
TEMPLATE = '{% sorted_keys_for k in hash %}{{ k }}:{{ hash[k] }} {% endsorted_keys_for %}'
REVERSED = '{% sorted_keys_for k in hash reversed %}{{ k }}:{{ hash[k] }} {% endsorted_keys_for %}'
def test_empty
assert_template_result('', TEMPLATE, 'hash' => {})
end
def test_single_element
assert_template_result('a:0 ', TEMPLATE, 'hash' => {'a' => 0})
end
def test_multiple_elements
assert_template_result('a:0 b:1 c:2 ', TEMPLATE, 'hash' => {'a' => 0, 'b' => 1, 'c' => 2})
end
def test_reversed
assert_template_result('c:2 b:1 a:0 ', REVERSED, 'hash' => {'a' => 0, 'b' => 1, 'c' => 2})
end
end
class SortedForTagCaseSensitivityTest < Test::Unit::TestCase
include Liquid
def test_default_is_case_insensitive
assert_template_result(
'alpha Beta charlie ',
'{% sorted_for i in array %}{{ i }} {% endsorted_for %}',
'array' => ['charlie', 'alpha', 'Beta'])
end
def test_case_sensitive
assert_template_result(
'Beta alpha charlie ',
'{% sorted_for i in array case_sensitive:true %}{{ i }} {% endsorted_for %}',
'array' => ['charlie', 'alpha', 'Beta'])
end
end
class SortedForNestedContainerTest < Test::Unit::TestCase
include Liquid
def test_nested_list_sorted_for
assert_template_result(
'1 2 3 4 5 6 7 8 9 ',
'{% for i in (0..2) %}{% assign subnested = nested[i] %}' \
'{% sorted_for j in subnested %}{{ j }} {% endsorted_for %}{% endfor %}',
'nested' => [[3, 2, 1], [6, 5, 4], [9, 8, 7]])
end
def test_nested_parallel_hash_sorted_keys_for
assert_template_result(
'a (one two three ) b (four five six ) c (seven eight nine ) ',
'{% sorted_keys_for i in nested %}{{ i }} (' \
'{% assign subnested = nested[i] %}'\
'{% sorted_keys_for j in subnested %}{{ subnested[j] }} ' \
'{% endsorted_keys_for %}) {% endsorted_keys_for %}',
'nested' => {
'b'=>{6=>"six", 5=>"five", 4=>"four"},
'c'=>{9=>"nine", 7=>"seven", 8=>"eight"},
'a'=>{2=>"two", 3=>"three", 1=>"one"}})
end
def test_nested_hash_of_hashes_sorted_keys_for
assert_template_result(
'a (one two three ) b (four five six ) c (seven eight nine ) ',
'{% sorted_keys_for i in nested %}' \
'{% assign outer = nested[i] %}{{ outer.key }} (' \
'{% sorted_keys_for j in outer.values %}{{ outer.values[j] }} ' \
'{% endsorted_keys_for %}) {% endsorted_keys_for %}',
'nested' => {
'B'=>{'key'=>'b', 'values'=>{6=>"six", 5=>"five", 4=>"four"}},
'C'=>{'key'=>'c', 'values'=>{9=>"nine", 7=>"seven", 8=>"eight"}},
'A'=>{'key'=>'a', 'values'=>{2=>"two", 3=>"three", 1=>"one"}}})
end
end
@rebelzach

This comment has been minimized.

Copy link

@rebelzach rebelzach commented Nov 19, 2013

Hey are you interested in moving this into a repository? I've got a bug that I'd like to be able to send in a pull request for. I'm running into an error regarding subdirectories in gists (specifically test/) and I can't push changes to my own fork.

@jwasham

This comment has been minimized.

Copy link

@jwasham jwasham commented Dec 20, 2013

Thank you this plugin was just what I needed!

@dbc60

This comment has been minimized.

Copy link

@dbc60 dbc60 commented May 15, 2014

Thank you so much!. This plug-in is great. Now I can sort my articles by date (I have an articles collection in addition to blog posts).

@arrdem

This comment has been minimized.

Copy link

@arrdem arrdem commented Jul 7, 2014

Awesome plugin, pretty silly that this isn't in the Jekyll core already. 👊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment