14
votes

I'm trying to pick a random element from an array -- is this possible using Liquid/Jekyll?

I can create an array -- and access a given index ... but is there a way to "shuffle" the array and then select an index, and thus get a random element from the array?

prefix: ["Foo", "Bar", "Baz"]
---

{{ page.prefix[1] }}

# outputs "Bar"
6
This question now has an answer here: stackoverflow.com/a/28323813/2838033 - adding this in case someone comes from Google.mechalynx

6 Answers

12
votes

The 2018 answer is

{% assign prefix = page.prefix | sample: 2 %}
{{ prefix[0] }}

As the OP asked about Jekyll, this can be found at: https://jekyllrb.com/docs/templates/

11
votes

Liquid doesn't have a filter for picking a random element from an array or an integer interval.

If you want Jekyll to do that, you would have to create an extension to add that liquid filter.

However, I must point out that doing so would pick a random element every time the page is generated, but not every time the page is viewed.

If you want to get different random values every time you visit a page, your best option is using javascript and letting the client pick a random value. You can use liquid to generate the relevant javascript though.

2
votes

You can create a plugin to get a random element. Something like this:

module Jekyll
  module RandomFilter
    # Use sample to get a random value from an array
    #
    # input - The Array to sample.
    #
    # Examples
    #
    #   random([1, 2, 3, 4, 5])
    #   # => ([2])
    #
    # Returns a randomly-selected item out of an array.
    def random(input)
      input.sample(1)
    end
  end
end

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

Then do something like this in your template to implement:

{% assign myArray = '1|2|3|4|5 | split: '|' %}
{% assign myNumber = myArray | random %}
2
votes

You may be able to do that just in Liquid, but it could less of generic solution like the one provided by @Brendan. According to this article, you can generate a random liquid number between min & max. So simply:

  • Assign the min to 0 and max to your array's length.
  • Loop over the array till you find your random number and pick you element.

Here is an example, get your random array index:

{% assign min = 0 %}
{% assign max = prefix.size %}
{% assign diff = max | minus: min %}
{% assign randomNumber = "now" | date: "%N" | modulo: diff | plus: min %}

Then find your random value:

{{ prefix[randomNumber] }}
1
votes

Without using a plugin (which might be a requirement if you are using github pages for example) and don't want the choice to be set only at build/rebuild time.

This uses collections as it's data source and some feature flags set in the page front matter.

{% if page.announcements %}
    <script>
        // homepage callout
        var taglines=[ 
         {% for txt in site.announcements %} 
           {{ txt.content | markdownify | jsonify | append: "," }}
        {% endfor %}
        ]
        var selection = document.querySelector('#tagline') !== null;
        if(selection) {
            document.querySelector('#tagline').innerHTML = taglines[ Math.floor(Math.random()*taglines.length) ];
        }
     </script>
{% endif %}

I use markdownify to process the content, jsonify to make it JavaScript safe and then append a comma to make my array.

The Javascript then populates one randomly at page load.

Add collection to config.yml

collections:
   announcements:

Add flag to page

---
layout: home
title: 
slider: true
announcements: true    
---

collection content item (test.md)

---    
published: true
---

This is a test post
0
votes

You could adapt Liquid::Drop and whitelist Ruby's sample method.

See https://github.com/Shopify/liquid/blob/master/lib/liquid/drop.rb#L69:

You would need to change:

blacklist -= [:sort, :count, :first, :min, :max, :include?]

to:

blacklist -= [:sort, :count, :first, :min, :max, :include?, :sample]

Next you could just use:

{{ some_liquid_array.sample }}