Perl Weekly Challenge 180: First Unique Character and Trim List
These are some answers to the Week 180 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 Sept. 4, 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: First Unique Character
You are given a string, $s
.
Write a script to find out the first unique character in the given string and print its index (0-based).
Example 1
Input: $s = "Perl Weekly Challenge"
Output: 0 as 'P' is the first unique character
Example 2
Input: $s = "Long Live Perl"
Output: 1 as 'o' is the first unique character
First Unique Character in Raku
This is a straight-forward, no-frills solution. I thought that I could write a better (or, at least, simpler) solution using features such as ArrayHash or Hash::Ordered, but this did not seem to simplify the syntax. Similarly, I initially wanted to use the built-in first routine, but it only made things more complicated. So, I ended up with a plain-vanilla solution using a hash (the %h
letter histogram) and an array (the @let
array) in parallel, and a hand-made loop to look for the first unique letter.
for "Perl Weekly Challenge", "Long Live Perl" -> $test {
my @let = $test.comb;
my %h; # histogram of letters
%h{$_}++ for @let;
say "$test: $_" and last if %h{@let[$_]} == 1 for 0..@let.end;
}
This script displays the following output:
$ raku ./first_unique.raku
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Perl
The good thing of having used a plain-vanilla solution in Raku is that it can be ported directly to Perl (and other languages) with only small changes. We’re using a hash (the %h
letter histogram) and an array (the @let
array) in parallel, and a hand-made loop to look for the first unique letter.
use strict;
use warnings;
use feature qw/say/;
for my $test ("Perl Weekly Challenge", "Long Live Perl") {
my @let = split //, $test;
my %h;
$h{$_}++ for @let;
for my $i (0..$#let) {
say "$test: $i" and last if $h{$let[$i]} == 1;
}
}
This script displays the following output:
$ perl ./first_unique.pl
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Other Languages
The following is a series of implementations of the First Unique Character task in 9 guest languages as follows:
- D
- Julia
- Javascript
- Kotlin
- Lua
- Python
- Ring
- Ruby
- Scala
First Unique Character in D
import std.stdio;
import std.array;
void main() {
string[] tests = [ "Perl Weekly Challenge", "Long Live Perl" ];
foreach(test; tests) {
int[string] histo;
string[] chars = test.split("");
foreach (ch; chars) {
histo[ch]++;
}
for (int i = 0; i < chars.length; i++) {
if (histo[chars[i]] == 1) {
writeln(test, ": ", i);
break;
}
}
}
}
Output:
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Julia
Note, if you did not guess it, that the only
function converts a one-letter string into a char. Also remember that Julia arrays start at index 1. The task asks us to provide the 0-based index of the first unique character. This is unnatural in Julia, so we have to subtract 1 from the Julia index to get a 0-based index.
for test in [ "Perl Weekly Challenge", "Long Live Perl" ]
histo = Dict()
letters = split(test, "")
for ch in test
histo[ch] = if (haskey(histo, ch)) histo[ch]+1 else 1 end
end
for i in 1:length(letters)
if (histo[only(letters[i])] == 1)
println(test, ": ", i-1)
break
end
end
end
Note that this construct:
histo[ch] = if (haskey(histo, ch)) histo[ch]+1 else 1 end
makes it possible to avoid the stylistically horrible if ... else
five-code-line construct.
Output:
$ julia ./first_unique.jl
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Javascript
let tests = [ "Perl Weekly Challenge", "Long Live Perl" ]
for (let i = 0; i < tests.length; i++) {
test = tests[i]
histo = new Map();
chars = test.split("")
chars.forEach( char => {
histo.set(char, histo.has(char) ? histo.get(char) + 1 : 1)
})
for (let i = 0; i < chars.length; i++) {
if (histo.get(chars[i]) == 1) {
console.log(test, ": ", i)
break
}
}
}
Output:
Perl Weekly Challenge : 0
Long Live Perl : 1
First Unique Character in Kotlin
fun main() {
val tests = arrayOf("Perl Weekly Challenge", "Long Live Perl")
for (test in tests) {
var his = mutableMapOf<Char,Int>() // letter histogram
val chars = test.toCharArray()
for (ch in chars) {
his[ch] = if (his.containsKey(ch)) his[ch]!! + 1 else 1
}
for (i in 0..chars.size) {
if (his[chars[i]] == 1) {
println(test + ": " + i)
break
}
}
}
}
Output:
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Lua
for _, test in pairs{"Perl Weekly Challenge", "Long Live Perl"} do
local histo = {} -- letter histotogram
for ch in string.gmatch(test, ".") do
-- histo[ch] = histo[ch] == nil and 1 or histo[ch] + 1
histo[ch] = (histo[ch] or 0) + 1
end
i = 0
for ch in string.gmatch(test, ".") do
if histo[ch] == 1 then
print(test, ": ", i)
break
end
i = i + 1
end
end
Note that we’re using here the or
logical operator:
histo[ch] = (histo[ch] or 0) + 1
to avoid this stylistically horrible code where histo[ch]
is repeated no less that four times:
if histo[ch] == nil then
histo[ch] = 1
else
histo[ch] = histo[ch] + 1
end
This works because in Lua, the or
logical operator doesn’t return a Boolean value, but the value of the first expression if it evaluates to true, and the value of the second one otherwise.
Output:
Perl Weekly Challenge : 0
Long Live Perl : 1
First Unique Character in Python
"""Find the first unique character in the given string"""
for test in ["Perl Weekly Challenge", "Long Live Perl"]:
histo = dict()
for char in test:
histo[char] = histo.get(char, 0) + 1
for i in range(0, len(test)):
if histo[test[i]] == 1:
print(test, ": ", i)
break
Note that using the histo.get(char, 0)
expression provides a default 0 value when the key doesn’t exists, thereby avoiding an if ... else
construct.
Output:
$ python3 ./first_unique.py
Perl Weekly Challenge : 0
Long Live Perl : 1
First Unique Character in Ring
for test in [ "Perl Weekly Challenge", "Long Live Perl" ]
histo = []
for i = 1 to len(test)
histo[test[i]] = 0 + histo[test[i]] + 1
next
for i = 1 to len(test)
if histo[test[i]] = 1
see test + ": " + (i-1) + nl
exit
ok
next
next
Note that a 0-based index is unnatural in Ring, so we have to subtract 1 from the looping variable i
.
Output:
$ ring ./first_unique.ring
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Ruby
for test in ["Perl Weekly Challenge", "Long Live Perl"]
chars = test.split("")
# with new(0), 0 becomes the default value when a key is absent
histo = Hash.new(0)
for char in chars
histo[char] += 1
end
for i in 0..chars.length - 1
if histo[chars[i]] == 1
print test, ": ", i, "\n"
break
end
end
end
Here again, using the 0 parameter in the call to the constructor (Hash.new(0)
) tells Ruby to return a 0 default value when the key is absent.
Output:
Perl Weekly Challenge: 0
Long Live Perl: 1
First Unique Character in Scala
object firstUnique extends App {
import scala.collection.mutable.Map
val tests: List[String] =
List("Perl Weekly Challenge", "Long Live Perl")
for (test <- tests) {
val chars = test.split("")
var histo: Map[String, Int] = Map()
for (ch <- chars) {
if (histo.contains(ch)) {
histo(ch) = histo(ch) + 1
} else {
histo += (ch -> 1)
}
}
var continue = true
var i = 0
while (continue) {
if (histo(chars(i)) == 1) {
println(test + ": " + i)
continue = false
}
i = i + 1
}
}
}
Output:
Perl Weekly Challenge: 0
Long Live Perl: 1
Task 2: Trim List
You are given list of numbers, @n
and an integer $i
.
Write a script to trim the given list where element is less than or equal to the given integer.
Example 1
Input: @n = (1,4,2,3,5) and $i = 3
Output: (4,5)
Example 2
Input: @n = (9,0,6,2,3,8,5) and $i = 4
Output: (9,6,8,5)
This is quite easy using the built-in grep
function in Raku and Perl (or filter
in Python or Scala).
Trim List in Raku
Rather than filtering out items which are less than or equal to the input integer, we keep numbers that are larger than the input integer.
for [3, [1,4,2,3,5]], [4, [9,0,6,2,3,8,5]] -> $test {
my $i = $test[0];
my @nums = |$test[1];
say "i = $i; nums = @nums[] => ", grep { $_ > $i }, @nums;
}
This script displays the following output:
$ raku ./trim_list.raku
i = 3; nums = 1 4 2 3 5 => (4 5)
i = 4; nums = 9 0 6 2 3 8 5 => (9 6 8 5)
Trim List in Perl
use strict;
use warnings;
use feature qw/say/;
for my $test ( [3, [1,4,2,3,5]], [4, [9,0,6,2,3,8,5]] ) {
my $i = $test->[0];
my @nums = @{$test->[1]};
say "i = $i, nums = @nums => ", join " ", grep $_ > $i, @nums;
}
This script displays the following output:
$ perl ./trim_list.pl
i = 3, nums = 1 4 2 3 5 => 4 5
i = 4, nums = 9 0 6 2 3 8 5 => 9 6 8 5
Trim List in Other Languages
The following is a series of implementations of the Trim List task in 14 guest languages as follows:
- in Awk
- in C
- in D
- in Java
- in JavaScript
- in Julia
- in Go
- in Kotlin
- in Lua
- in Pascal
- in Python
- in Ring
- in Ruby
- in Scala
Trim List in Awk
Contrary to Raku and Perl, awk doesn’t have a grep
or filter
feature. So we implement a loop over the values and print out those that satisfy the condition. We’ll do that in several other languages. Also note that, since awk is essentially driven by external data, the data is passed by the shell to the awk script. On each line, the first item in the input integer to be compared the other values.
# run for example as:
# echo '3 1 4 2 3 5
# 4 9 0 6 2 3 85 ' | awk -f trim_list.awk
{
i = $1
printf "Input = %-18s- i = %d => result = ", $0, $1
for (j = 2; j <= NF; j++) {
if ( $j > i) {
printf "%d ", $j
}
}
print ""
}
Output:
$ echo '3 1 4 2 3 5
4 9 0 6 2 3 8 5 ' | awk -f trim_list.awk
Input = 3 1 4 2 3 5 - i = 3 => result = 4 5
Input = 4 9 0 6 2 3 8 5 - i = 4 => result = 9 6 8 5
Trim List in C
C doesn’t have a built-in grep
or filter
feature. So we implement a loop over the values and print out those that satisfy the condition. On each test line, the first item in the input integer to be compared the other values.
#include <stdio.h>
#include <string.h>
int main() {
const char tests[2][10] = {
{ 3, 1, 4, 2, 3, 5 },
{ 4, 9, 0, 6, 2, 3, 8, 5 }
};
for (int j = 0; j <=1; j++) {
char test[10];
memcpy(test, tests[j], sizeof(tests[j]));
int i = test[0];
printf("i = %d; nums = ", i);
/* printing input test array */
for (char k = 1; k < sizeof(test); k++) {
printf("%d ", test[k]);
}
printf(" => ");
/* printing the result */
for (char k = 1; k < sizeof(test); k++) {
if (test[k] > i) {
printf("%d ", test[k]);
}
}
printf("%s\n", "");
}
}
Output:
$ ./a.out
i = 3; nums = 1 4 2 3 5 0 0 0 0 => 4 5
i = 4; nums = 9 0 6 2 3 8 5 0 0 => 9 6 8 5
Trim List in D
import std.stdio;
void main() {
int[][][] tests = [[[3], [1,4,2,3,5]], [[4], [9,0,6,2,3,8,5]]];
foreach(test; tests) {
int i = test[0][0];
int[] nums = test[1];
write(i, " ", nums, " => ");
for (int j = 0; j < nums.length; j++) {
if (nums[j] > i) {
printf("%d ", nums[j]);
}
}
writeln("");
}
}
Output:
3 [1, 4, 2, 3, 5] => 4 5
4 [9, 0, 6, 2, 3, 8, 5] => 9 6 8 5
Trim List in Java
import java.util.Arrays;
public class TrimList {
private static final int[][][] tests =
{{{3}, {1,4,2,3,5}}, {{4}, {9,0,6,2,3,8,5}}};
public static void main(String[] args) {
for (int j = 0; j < tests.length; j++) {
int i = tests[j][0][0];
int[] nums = tests[j][1];
System.out.printf("i = %d; nums = %s => ",
i, Arrays.toString(nums));
for (int k = 0; k < nums.length; k++) {
if (nums[k] > i) {
System.out.printf("%d ", nums[k]);
}
}
System.out.printf("%s", "\n");
}
}
}
Output:
i = 3; nums = [1, 4, 2, 3, 5] => 4 5
i = 4; nums = [9, 0, 6, 2, 3, 8, 5] => 9 6 8 5
Trim List in JavaScript
tests = [[[3], [1,4,2,3,5]], [[4], [9,0,6,2,3,8,5]]];
for(let j = 0; j < tests.length; j++) {
let i = tests[j][0][0]
let nums = tests[j][1]
process.stdout.write("i= " + i + " nums= " + nums + " => ")
for (let k = 0; k < nums.length; k++) {
if ( nums[k] > i) {
process.stdout.write(nums[k] + " ")
}
}
process.stdout.write("\n")
}
Output:
i= 3 nums= 1,4,2,3,5 => 4 5
i= 4 nums= 9,0,6,2,3,8,5 => 9 6 8 5
Trim List in Julia
In my humble opinion, Julia is the best language used in this blog post after Raku and Perl. Just consider how concise and clear this implementation is, compared to most other languages. And it has very nice functional programming capabilities (e.g. the filter
built-in function below with its lambda). In addition, Julia runs fast. Its only drawback in my view is that the error messages emitted by the compiler or the runtime are really not clear (like most languages that compile to Java code).
for test in [[3, [1,4,2,3,5]], [4, [9,0,6,2,3,8,5]]]
i = test[1]
nums = test[2]
println( "i = $i, num = $nums => ", filter((x) -> x > i, nums))
end
Output:
$ julia ./trim_list.jl
i = 3, num = [1, 4, 2, 3, 5]: [4, 5]
i = 4, num = [9, 0, 6, 2, 3, 8, 5]: [9, 6, 8, 5]
Trim List in Go
package main
import "fmt"
func main() {
tests := [2][2][8]int{{{3}, {1, 4, 2, 3, 5}},
{{4}, {9, 0, 6, 2, 3, 8, 5}}
}
for j := 0; j < len(tests); j++ {
i := tests[j][0][0]
fmt.Printf("i = %d; nums = ", i)
nums := tests[j][1]
fmt.Print(nums, "; => ")
for k := 0; k < len(nums); k++ {
if nums[k] > i {
fmt.Printf("%d ", nums[k])
}
}
fmt.Println("")
}
}
Output:
i = 3; nums = [1 4 2 3 5 0 0 0]; => 4 5
i = 4; nums = [9 0 6 2 3 8 5 0]; => 9 6 8 5
Trim List in Kotlin
Declaring a multidimensional array in Kotlin is a pain in the neck (as well as in Scala, and it is even worse in Nim). Besides that, the implementation is quite straight forward.
import java.util.Arrays
fun main() {
val tests = arrayOf(arrayOf(intArrayOf(3,),
intArrayOf(1,4,2,3,5)),
arrayOf(intArrayOf(4,),
intArrayOf(9,0,6,2,3,8,5))
)
for (test in tests) {
print(Arrays.deepToString(test) + " => ")
var i = test[0][0]
var nums = test[1];
for (j in nums) {
if (j > i) {
print(j.toString() + " ")
}
}
println("")
}
}
Output:
[[3], [1, 4, 2, 3, 5]] => 4 5
[[4], [9, 0, 6, 2, 3, 8, 5]] => 9 6 8 5
Trim List in Lua
tests = {{{3}, {1,4,2,3,5}}, {{4}, {9,0,6,2,3,8,5}}}
for _, test in ipairs(tests) do
local i = test[1][1]
local nums = test[2]
io.write("i= ", i, "; nums= ", table.concat(nums, " "), " => ")
for _, j in ipairs(nums) do
if j > i then
io.write(j, " ")
end
end
print("")
end
Output:
i= 3; nums= 1 4 2 3 5 => 4 5
i= 4; nums= 9 0 6 2 3 8 5 => 9 6 8 5
Trim List in Pascal
In Pascal, since I declared the third dimension of the tests
array to be 0..6
, I had to fill the empty slots with zeroes. If there is a better way, I do not know it (I learned and used Pascal about 33 years ago and almost did not use it since, please forgive my ignorance). It not, then it is a major weakness of the language. Otherwise, the syntax is a bit verbose, but very clear.
program TrimList;
const tests : array[0..1,0..1,0..6] of integer =
(((3,0,0,0,0,0,0), (1,4,2,3,5,0,0)), ((4,0,0,0,0,0,0), (9,0,6,2,3,8,5)));
var
i, j, k: integer;
nums: array of integer;
begin
for j := 0 to length(tests) - 1 do
begin
i := tests[j][0][0];
write('i = ', i, '; nums = ');
nums := tests[j][1];
for k := 0 to length(nums) - 1 do
begin
write(nums[k], ' ');
end;
write(' => ');
for k := 0 to length(nums) - 1 do
begin
if nums[k] > i then
write(nums[k], ' ');
end;
writeln('')
end;
end.
Output:
i = 3; nums = 1 4 2 3 5 0 0 => 4 5
i = 4; nums = 9 0 6 2 3 8 5 => 9 6 8 5
Trim List in Python
About 19 years ago, I took up Perl and stopped writing in Python (my favorite language for a few years at that point), because I felt Perl was superior. I still think so, but I must admit Python is fairly expressive and concise.
for test in [3, [1,4,2,3,5]], [4, [9,0,6,2,3,8,5]]:
i = test[0]
nums = test[1]
print(i, nums, " => ", list(filter(lambda n: n>i, nums)))
Output:
$ python3 ./trim_list.py
3 [1, 4, 2, 3, 5] => [4, 5]
4 [9, 0, 6, 2, 3, 8, 5] => [9, 6, 8, 5]
Trim List in Ring
for test in [[3, [1,4,2,3,5]], [4, [9,0,6,2,3,8,5]]]
i = test[1]
nums = test[2]
see "i = " + i + " ; nums = "
for j in nums
see "" + j + " "
next
see " => "
for j in nums
if j > i
see "" + j + " "
ok
next
see " " +nl
next
Output:
$ ring ./trim_list.ring
i = 3 ; nums = 1 4 2 3 5 => 4 5
i = 4 ; nums = 9 0 6 2 3 8 5 => 9 6 8 5
Trim List in Ruby
for test in [ [3, [1,4,2,3,5]], [4, [9,0,6,2,3,8,5]] ]
i = test[0]
nums = test[1]
print "i = #{i}, nums = #{nums} => ", nums.select {|n| n > i}
puts " "
end
Output:
i = 3, nums = [1, 4, 2, 3, 5] => [4, 5]
i = 4, nums = [9, 0, 6, 2, 3, 8, 5] => [9, 6, 8, 5]
Trim List in Scala
As I said above, declaring multidimensional arrays is a pain in the neck in Scala. And its typing system can be a straitjacket. It’s a pity because Scala has very nice features in terms of combining object-oriented and functional programming paradigms.
object trimList extends App {
val tests: List[List[List[Int]]] = List(
List(List(3), List(1, 4, 2, 3, 5)),
List(List(4), List(9, 0, 6, 2, 3, 8, 5))
)
for (test <- tests) {
val i = test(0)(0)
val nums = test(1)
print(s"i: $i, nums: " + nums.mkString(" "))
println(" => " + nums.filter(_ > i).mkString(" "))
}
}
Output:
i: 3, nums: 1 4 2 3 5 => 4 5
i: 4, nums: 9 0 6 2 3 8 5 => 9 6 8 5
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 11, 2022. And, please, also spread the word about the Perl Weekly Challenge if you can.
Hey, I wonder if the output of the 1st case of the trim list challenge should be 4,3,5 instead of 4,5 as stated on the website?
Hi, thanks for your comment. The task requests us to "trim the given list where element is less than or equal to the given integer". So, in my view, items that are equal to the input integer (3 in this case) should be trimmed out.
You were right, the website is now updated too. Thanks for clarifying my doubt.