Computing a Weighted Average with Liquid Template Filters - jekyll

I'm using Jekyll with its Liquid Templates hosted on Github Pages and I'd like to compute a weighted average, but I don't see a way to deal with order of operations using Liquid math filters.
For example I'd like to compute something like:
{{ (page.x | times:.4) | plus:(page.y | times:.6) }}
--> (x * .4) + (y * .6)
But it seems the parenthesis are ignored an I'm effectively getting:
{{ page.x | times:.4 | plus:page.y | times:.6 }}
--> (((x * .4) + y) * .6)

Liquid filters are applied one at a time in the order they appear, using the former value. You cannot change precedence with parenthesis (you can only do this with if conditionals).
Here you have to split you operation like this :
{% assign a = x | times:.4 %}
{% assign b = y | times:.6 %}
{% assign c = a | plus: b %}

Related

Getting max from a string list in jinja2

I would like to get a max value from a list. I have a homeassistant configuration that I would like to work, but for some reason list is considered as string value, and I get ] character as max value:
{% set value = ['nema','niska','nema'] %}
{% set list = value | replace("nema", 0 ) | replace("niska", 1 | int) | replace("visoka", 2) | replace("vrlovisoka", 3) %}
{{ list | max }}
If I use {{ list | map('int') | max }} like other posts suggest, I just get an error ValueError: Template error: int got invalid input '[' when rendering template
How can I make this work?

Convert accent characters to normal characters in Liquid

For instance name = Florian Müllner, want name to be Florian MUllner
How to covert name with accented characters in Liquid?
Read the replace doc, but was not able to figure out. How to use?
This is my very very dirty solution and far from complete (but works for my needs) with no plugins in Jekyll:
{% assign text = 'Müller Pérez' %}
{% include normalize_text.html %}
and the included file works as a function:
{% assign text = text | replace: 'á', 'a' | replace: 'é', 'e' | replace: 'í', 'i' | replace: 'ó', 'o' | replace: 'ú', 'u' %}
{% assign text = text | replace: 'à', 'a' | replace: 'è, 'e' | replace: 'ì', 'i' | replace: 'ò', 'o' | replace: 'ù', 'u' %}
{% assign text = text | replace: 'ä', 'a' | replace: 'ë', 'e' | replace: 'ï', 'i' | replace: 'ö', 'o' | replace: 'ü', 'u' %}
{% assign text = text | replace: 'â', 'a' | replace: 'ê, 'e' | replace: 'î', 'i' | replace: 'ô', 'o' | replace: 'û', 'u' %}
You can use replace like this.
{% assign text = 'Florian Müllner' | replace: "ü", "U" %}

How can I add a random number method in liquid that can called multiple times?

How can I add a random number in my liquid template files?
Is there something similar to PHP's rand()?
I have seen this used before, but if the variable is called in other places it displays the same number and is not randomized, even if multiple variables are created.
{% assign min = 65 %}
{% assign max = 80 %}
{% assign diff = max | minus: min %}
{% assign randomNumber = "now" | date: "%N" | modulo: diff | plus: min %}
This is how I am trying to accomplish it now:
<div class="contentDoubleWideWrap">
<div class="contentDoubleWideWrap">
{% assign min = 50 %}
{% assign max = 1000000 %}
{% assign diff = max | minus: min %}
{% assign randomNumber = site.time | date: "%s" | modulo: diff | plus: min %}
<div id="div-gpt-ad-1553024073723-{{ randomNumber }}" data-ad="div-gpt-ad-1553024073723-0" class="tmsads adContentDouble"></div>
</div>
<div class="contentDoubleWideWrap">
{% assign min2 = 50 %}
{% assign max2 = 1000000 %}
{% assign diff2 = max2 | minus: min2 %}
{% assign randomNumber2 = site.time | date: "%s" | modulo: diff2 | plus: min2 %}
<div id="div-gpt-ad-1553024121524-{{ randomNumber2 }}" data-ad="div-gpt-ad-1553024121524-0" class="tmsads adContentDouble"></div>
</div>
</div>
What you could do, it to define a prng, for example a linear congruential random number generator.
This works the following way:
you need a random seed (this can be date: "%N"),
you need the parameters of the rng: m, a, c
you need to specify the index into the generated pseudorandom series.
A good set of parameters:
m=2^31
a=1103515245
c=12345
This is used by GCC rand, see here for the link:
https://en.wikipedia.org/wiki/Linear_congruential_generator
the random number is created by (in pseudo code):
let x=seed
loop index times:
x=(a*x+c)%m
In this setting the random number you get from the generator can be further conditioned by the max, min, module parameters like you already did this for the date.
The only difference would be to use x instead of assign randomNumber = site.time | date: "%s"
You have almost everything in your code to do this. If you only need a few random numbers I would most probably simply unroll the loop in the pseudocode. Does that work for you?
Let me show how to implement the example you gave above:
<div class="contentDoubleWideWrap">
<div class="contentDoubleWideWrap">
{% assign min = 50 %}
{% assign max = 1000000 %}
{% assign diff = max | minus: min %}
{% assign seed = site.time | date: "%N" %}
{% assign randomNumber1 = seed | modulo: diff | plus: min %}
<div id="div-gpt-ad-1553024073723-{{ randomNumber1 }}" data-ad="div-gpt-ad-1553024073723-0" class="tmsads adContentDouble"></div>
</div>
<div class="contentDoubleWideWrap">
{% assign m = 2147483648 %}
{% assign a = 1103515245 %}
{% assign c = 12345 %}
{% assign min2 = 50 %}
{% assign max2 = 1000000 %}
{% assign diff2 = max2 | minus: min2 %}
{% assign randomNumber2 = seed | times: a | plus: c | modulo: m | modulo: diff2 | plus: min2 %}
<div id="div-gpt-ad-1553024121524-{{ randomNumber2 }}" data-ad="div-gpt-ad-1553024121524-0" class="tmsads adContentDouble"></div>
</div>
</div>
In this case x0 is seed and x1 the next state of the rng, you get randomNumber2 from that by modulo diff |plus: min2. To generate randomNumber3, you would repeate the a c and m filters one more time, like:
times: a | plus: c | modulo: m |times: a | plus: c | modulo: m
This can work if you only have a few random numbers to generate.
If you need more random numbers, you can use a loop instead to do the same thing.
The problem is that basically a static site is generated. What you could do is to move the random number generation to the client side, by using javascript. Does that work for you?
I bet it is possible via plugins, e.g. https://github.com/codecalm/jekyll-random
random_number(index, min=0, max=100, round=0)
Returns a number between min and max based on index. By default it returns a number between 0 and 100.
{% for i in (1..100) %}
{{ i }} - {{ forloop.index | random_number: 0, 10 }}
{% endfor %}
The code above returns random numbers like:
1 - 6
2 - 1
3 - 6
4 - 8
5 - 4
6 - 7
7 - 1
...

Formatting numbers in Jekyll without using custom plugins

I am using a Jekyll collection to manage elements of a lesson. Each element has a duration in its YAML front matter:
---
duration: 5
---
I loop over the elements to figure out the start time for each element:
{% assign current = 0 %}
<table>
{% for element in site.elements %}
<tr>
<td>{{ forloop.index }}</td>
<td>{{ element.title }}</td>
<td>{{ current | divided_by: 60 }}:{{ current | modulo: 60 }}</td>
</tr>
{% assign current = current | plus: element.duration %}
{% endfor %}
</table>
This gives the right output, but is poorly formatted: in particular, some times are shown like this:
9:5
instead of:
09:05
I can't think of a way to use date formatting to get the output I want, since the "times" are just minutes, not dates. Is there a way to format numbers in Jekyll with a specified number of decimal places and leading zeroes?
You can use conditional formating:
{% assign h = timeInMinutes | divided_by: 60 %}
{% if h <= 10 %}{% assign h = h | prepend: '0' %}{% endif %}
{% assign m = timeInMinutes | modulo: 60 %}
{% if m <= 10 %}{% assign m = m | prepend: '0' %}{% endif %}
{{ h }}:{{ m }}
Or a string trick :
{{ timeInMinutes | divided_by: 60 | plus: 100 | slice: 1, 2 }}
:
{{ timeInMinutes | modulo: 60 | plus: 100 | slice: 1, 2 }}

Get the difference in days between two dates in Jekyll

I would like to get the difference in days between two dates in Jekyll. How can I achieve this?
{% capture currentDate %}{{ site.time | date: '%Y-%m-%d' }}{% endcapture %}
{{currentDate}}
{% capture event_date %}{{ entry.date }}{% endcapture %}
{% if event_date < currentDate %}Yes{% else %}No{% endif %}
In the entry there is my YAML:
---
title: ChartLine C3
type: charts
description: Chart with round for prisma
id: c3-1
date: 2015-07-18
---
The way to do this in Liquid (Jekyll's templating engine) is silly:
{% assign today = site.time | date: '%s' %}
{% assign start = '20-01-2014 04:00:00' | date: '%s' %}
{% assign secondsSince = today | minus: start %}
{% assign hoursSince = secondsSince | divided_by: 60 | divided_by: 60 %}
{% assign daysSince = hoursSince | divided_by: 24 %}
Hours: {{hoursSince}}
Days: {{daysSince}}
Hours: 27780
Days: 1157
Note that Liquid's divide_by operation rounds automatically.
Remainder hours: {{hoursSince | modulo: 24}}
Remainder hours: 12
If this annoys you as much as it annoyed me then you can do this to recover decimal places:
{% assign k = 10 %}
{% assign divisor = 24 %}
{% assign modulus = hoursSince | modulo: 24 | times: k | divided_by: divisor %}
{{daysSince}}.{{modulus}}
1157.5
Add more zeroes to k to add more decimal places.
No-one actually answered the question but it's not impossible.
You can get the difference between years, say how many years have passed since the year 2000 would be:
{{ site.time | date: '%Y' | minus:2000 }}
As for days between two dates, that's harder.. best bet is to look at the plugin:
https://github.com/markets/jekyll-timeago
It's output might be a bit verbose though but you could modify the plugin itself (have a look through the code, it's not too complex)
If all you want to do is know whether a date from your Front Matter is earlier than the system time then you can use the ISO 8601 date format and rely on the lexicographical ordering. It's sort of cheating but it'll work for the example you provided.
It's important to capture both site.time and the date from your Front Matter (page.past_date and page.future_date in the example below) in ISO 8601 format in order for this trick to work.
---
layout: default
past_date: 2015-03-02
future_date: 2016-03-02
---
{% capture currentDate %}{{ site.time | date: '%F' }}{% endcapture %}
{% capture pastDate %}{{ page.past_date | date: '%F' }}{% endcapture %}
{% capture futureDate %}{{ page.future_date | date: '%F' }}{% endcapture %}
<br>currentDate: {{currentDate}}
<br>PastDate earlier than currentDate? {% if pastDate < currentDate %}Yes{% else %}No{% endif %}
<br>FutureDate earlier than currentDate? {% if futureDate < currentDate %}Yes{% else %}No{% endif %}
Gives me the following output:
currentDate: 2015-07-12
PastDate earlier than currentDate? Yes
FutureDate earlier than currentDate? No
I computed the difference in years between current date and the one of the post to render a specific message, and it works pretty nicely:
{% assign currentYear = site.time | date: '%Y' %}
{% assign postYear = post.date | date: '%Y' %}
{% assign postAge = currentYear | minus: postYear | minus: 1 %}
{% if postAge >= 3 %}
<div class="maybe-obsolete">
This post has been published more than {{ postAge }} years ago, and parts
of its contents are very likely to be obsolete by now.
</div>
{% endif %}