Perl Weekly Challenge 207: Keyboard Word and H-Index

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

Spoiler Alert: This weekly challenge deadline is due in a few days from now (on March 12, 2023 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: Keyboard Word

You are given an array of words.

Write a script to print all the words in the given array that can be types using alphabet on only one row of the keyboard.

Let us assume the keys are arranged as below:

Row 1: qwertyuiop
Row 2: asdfghjkl
Row 3: zxcvbnm

Example 1

Input: @words = ("Hello","Alaska","Dad","Peace")
Output: ("Alaska","Dad")

Example 2

Input: @array = ("OMG","Bye")
Output: ()

Note that in the examples above, the input words are in title case (initial upper case letter), so we will need to perform a case conversion somewhere If we want to obtain any match.

Keyboard Word in Raku

First, we create an array of three hashes to store the letters of each keyboard row.

Then, the find-kb-word subroutine has two nested loops to loop over the input words and the keyboard rows. It then uses an all junction for each word/row combination to check whether letters of a word all belong to the same key row.

my @rows;
push @rows, %(map { $_ => True }, $_.comb) 
    for "qwertyuiop", "asdfghjkl", "zxcvbnm";

sub find-kb-word (@in) {
    my @out;
    for @in -> $word {
        for @rows -> %row {
            push @out, $word and next 
                if %row{all $word.lc.comb}:exists;
        }
    }
    return @out;
}
for <Hello Alaska Dad Peace>, <OMG Bye>, 
    <Power Fad Finish Tower Quit True Omit> -> @test {
    say find-kb-word @test;
}

This program displays the following output:

$ raku ./keyboard-words.raku
[Alaska Dad]
[]
[Power Fad Tower Quit True]

Keyboard Word in Perl

This is a port to Perl of the above Raku program. The only significant difference is that, since Perl doesn’t have junctions, the find_kb_word subroutine uses a grep to find whether all letters of a word belong to the same key row.

use strict;
use warnings;
use feature "say";

my @rows;
push @rows, {map {$_ => 1} split //, $_} 
    for "qwertyuiop", "asdfghjkl", "zxcvbnm";

for my $test ([<Hello Alaska Dad Peace>], [<OMG Bye>], 
    [<Power Fad Finish Tower Quit True Omit>]) {
    say join " ", find_kb_word(@$test);
}

sub find_kb_word {
    my @out;
    for my $word (@_) {
        for my $row (@rows) {
            my $eligible = 1;
            push @out, $word and last 
                unless grep {not exists $row->{$_}} 
                split //, lc $word;
        }
    }
    return @out ? @out : "()";
}

This program displays the following output:

$ perl ./keyboard-words.pl
Alaska Dad
()
Power Fad Tower Quit True

Task 2: H-Index

You are given an array of integers containing citations a researcher has received for each paper.

Write a script to compute the researcher’s H-Index. For more information please checkout the Wikipedia page.

The H-Index is the largest number h such that h articles have at least h citations each. For example, if an author has five publications, with 9, 7, 6, 2, and 1 citations (ordered from greatest to least), then the author’s h-index is 3, because the author has three publications with 3 or more citations. However, the author does not have four publications with 4 or more citations.

Example 1

Input: @citations = (10,8,5,4,3)
Output: 4

Because the 4th publication has 4 citations and the 5th has only 3.

Example 2

Input: @citations = (25,8,5,3,3)
Output: 3

The H-Index is 3 because the fourth paper has only 3 citations.

H-Index in Raku

The h-index subroutine first sorts the input data in descending order. It then looks for the first item whole value is less that its index + 1 and returns it.

sub h-index (@citations) {
    my @ordered = @citations.sort.reverse;
    for 0..@ordered.end -> $i {
        return $i if $i+1 > @ordered[$i];
    }
    # If we get here, then all papers qualify
    return @ordered.elems;
}
for <10 8 5 4 3>, <25 8 5 3 3>, <12 10 9 5 11> -> @test {
  say "@test[]".fmt("%-15s => "), h-index @test;
}

This program displays the following output:

$ raku ./h-index.raku
10 8 5 4 3      => 4
25 8 5 3 3      => 3
12 10 9 5 11    => 5

H-Index in Perl

This is a port to Perl of the above Raku program.

use strict;
use warnings;
use feature "say";

sub h_index {
    my @ordered = sort { $b <=> $a } @_;
    for my $i (0..$#ordered) {
        return $i if $i+1 > $ordered[$i];
    }
    # If we get here, then all papers qualify
    return scalar @ordered;
}
for my $test ([<10 8 5 4 3>], [<25 8 5 3 3>], [<12 10 9 5 11>]) {
    printf "%-15s => %d\n", "@$test", h_index @$test;
}

This program displays the following output:

$ perl h-index.pl
10 8 5 4 3      => 4
25 8 5 3 3      => 3
12 10 9 5 11    => 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 March 19, 2023. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.