## Perl Weekly Challenge 179: Ordinal Numbers and Unicode Sparkline

These are some answers to the Week 179 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few of days from now (on Aug. 28, 2022 at 23:59). This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.

## Task 1: Ordinal Number Spelling

You are given a positive number, `\$n`.

Write a script to spell the ordinal number.

For example,

``````11 => eleventh
62 => sixty-second
99 => ninety-ninth
``````

Hum, this task is not very interesting, since it has more to do with English than with computer science. I’m not going to enumerate dozens of numeral or ordinal English names. So, contrary to what I usually do, I’ll use an off-the-shelf module to complete this task.

### Ordinal Number Spelling in Raku

Here, we use the `Lingua::EN::Numbers` Raku module:

``````use Lingua::EN::Numbers;

for 11, 62, 99 -> \$num {
say "\$num => ", ordinal(\$num);
}
``````

This program displays the following output:

``````\$ raku ./ordinal_numbers.raku
11 => eleventh
62 => sixty-second
99 => ninety-ninth
``````

### Ordinal Number Spelling in Perl

The Perl module we’re going to use is also called `Lingua::EN::Numbers`, but it’s not the same package as the Raku module used above.

``````use strict;
use warnings;
use feature qw/say/;

use Lingua::EN::Numbers qw/num2en_ordinal/;

for my \$num (11, 62, 99) {
say "\$num => ", ordinal(\$num);
}
``````

This program displays the following output:

``````\$ perl ./ordinal_numbers.pl
11 => eleventh
62 => sixty-second
99 => ninety-ninth
``````

You are given a list of positive numbers, `@n`.

Write a script to print sparkline in Unicode for the given list of numbers.

Here, I regret to have to say that my friend Mohammad has been a bit sloppy, since he did not explain anything about sparklines (I had never heard about sparklines before). A sparkline is a very small graph of successive values laid out horizontally where the height of the line is proportional to the values in succession. See this Wikipedia Page for additional information.

Use the following series of Unicode characters to create a program that takes a series of numbers separated by one or more whitespace or comma characters and generates a sparkline-type bar graph of the values on a single line of output.

The eight characters: ‘▁▂▃▄▅▆▇█’ (Unicode values U+2581 through U+2588).

We have eight Unicode characters of growing size, so the problem essentially boils down to scaling the input sequence to eight steps.

### Unicode Sparkline in Raku

``````my @bars = map {.chr}, 0x2581 .. 0x2588;
for < 2 4 6 8 10 12 10 8 6 4 2>, <0 1 19 20>,
<0 999 4000 4999 7000 7999> -> @test {
my (\$min, \$max) = @test.minmax[0,*-1];
say "@test[]; min: \$min; max: \$max.";
say join '', @bars[ map { @bars * (\$_ - \$min) / (\$max - \$min) min @bars.end }, @test], "\n";
}
``````

This program displays the following output:

``````\$ raku ./sparkline.raku
2 4 6 8 10 12 10 8 6 4 2; min: 2; max: 12.
▁▂▄▅▇█▇▅▄▂▁

0 1 19 20; min: 0; max: 20.
▁▁██

0 999 4000 4999 7000 7999; min: 0; max: 7999.
▁▁▅▅██
``````

### Unicode Sparkline in Perl

``````use strict;
use warnings;
use feature qw/say/;

binmode(STDOUT, ":utf8");
my @bars = map chr, 0x2581 .. 0x2588;

for my \$test ([< 2 4 6 8 10 12 10 8 6 4 2>],
[<0 1 19 20>], [<0 999 4000 4999 7000 7999>]) {
my @test = @\$test;
my (\$min, \$max) = (sort {\$a <=> \$b} @\$test)[0, \$#test];
my \$out = "";
for my \$item (@test) {
my \$h = @bars * (\$item - \$min) / (\$max - \$min);
\$h = \$#bars if \$h > \$#bars;
\$out .= \$bars[int(\$h)];
}
say "@test; min: \$min; max: \$max.";
say \$out, "\n";
}
``````

This program displays the following output:

``````\$ perl sparkline.pl
2 4 6 8 10 12 10 8 6 4 2; min: 2; max: 12.
▁▂▄▅▇█▇▅▄▂▁

0 1 19 20; min: 0; max: 20.
▁▁██

0 999 4000 4999 7000 7999; min: 0; max: 7999.
▁▁▅▅██
``````

### Unicode Sparkline in Julia

``````function sparkline(test)
bars = '\u2581':'\u2588'
bar_count = length(bars)
min, max = extrema(test)
out = ""
for item in test
h = 1 + bar_count * (item - min) / (max - min)
h > bar_count && (h = bar_count)
out = out * string(bars[Int(floor(h))])
end
return out * "\n"
end

tests = [ [2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2],
[0, 1, 19, 20], [0, 999, 4000, 4999, 7000, 7999] ]
for test in tests
println(test, "\n")
println( sparkline(test))
end
``````

Output:

``````\$ julia ./sparkline.jl
[2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2]

▁▂▄▅▇█▇▅▄▂▁

[0, 1, 19, 20]

▁▁██

[0, 999, 4000, 4999, 7000, 7999]

▁▁▅▅██
``````

### Unicode Sparkline in Python

``````def sparkline(test):
bars = [chr(bar) for bar in range(9601, 9608+1)]
minim = min(test)
maxim = max(test)
scale = maxim - minim
length = len(bars)
line = ""
for item in test:
line += bars[min(int((item-minim) / scale * length), length - 1)]
return line

tests = [ [2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2], \
[0, 1, 19, 20], [0, 999, 4000, 4999, 7000, 7999] ]

for test in tests:
print(test)
print(sparkline(test), "\n")
``````

Output:

``````\$ python3 sparkline.py
[2, 4, 6, 8, 10, 12, 10, 8, 6, 4, 2]
▁▂▄▅▇█▇▅▄▂▁

[0, 1, 19, 20]
▁▁██

[0, 999, 4000, 4999, 7000, 7999]
▁▁▅▅██
``````

### Unicode Sparkline in Ruby

``````bars = ('▁'..'█').to_a
tests = [ [1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1],
[0, 1, 19, 20], [0, 999, 4000, 4999, 7000, 7999] ]
for test in tests
min, max = test.minmax
puts test.join(" ")
puts "min: %.2f; max: %.2f"% [min, max]
scale = (max - min) / (bars.size - 1)
line = ""
for item in test
h = bars.size * (item - min) / (max - min)
if h >= bars.size
h = bars.size - 1
end
line += bars[h]
end
puts line
puts " "
end
``````

Output:

``````1 2 3 4 5 6 7 8 7 6 5 4 3 2 1
min: 1.00; max: 8.00
▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

0 1 19 20
min: 0.00; max: 20.00
▁▁██

0 999 4000 4999 7000 7999
min: 0.00; max: 7999.00
▁▁▅▅██
``````

### Unicode Sparkline in Scala

``````object sparkLine extends App {

def sparkline(test: Array[Int]): String = {
val bars = ('\u2581' to '\u2588')
var outl = ""
for (item <- test) {
var h = bars.length * (item - test.min) / (test.max - test.min)
if (h >= bars.length) { h = bars.length - 1 }
outl = outl + bars(h)
}
return outl
}
val tests = Array(
Array(1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1),
Array(0, 1, 19, 20),
Array(0, 999, 4000, 4999, 7000, 7999)
)
for (test <- tests) {
println(test.mkString(" "))
println("")
println(sparkline(test))
println("")
}
}
``````

Output:

``````1 2 3 4 5 6 7 8 7 6 5 4 3 2 1

▁▂▃▄▅▆▇█▇▆▅▄▃▂▁

0 1 19 20

▁▁██

0 999 4000 4999 7000 7999

▁▁▅▅██
``````

## Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on September 4, 2022. And, please, also spread the word about the Perl Weekly Challenge if you can.