Perl Weekly Challenge: The Weakest Split

I don’t know, this week there’s no lyrics that the problems are inspiring. The closest I can get is Shout by The Isley Brothers, so let’s go with that…

Let’s dive into Perl Weekly Challenge 253!

Task 1: Split Strings

You are given an array of strings and a character separator.

Write a script to return all words separated by the given character excluding empty string.

Example 1

Input: @words = ("one.two.three","four.five","six")
       $separator = "."
Output: "one","two","three","four","five","six"

Example 2

Input: @words = ("$perl$$", "$$raku$")
       $separator = "$"
Output: "perl","raku"

Approach

This is fairly straightforward: split strings on the separator, filter out empty strings.

Raku

The :skip-empty named parameter handles filtering out the empty strings.

sub splitOnSeparator(@words, $separator) {
  my @output;
  for @words -> $str {
    @output.append( $str.split($separator, :skip-empty) );
  }
  return @output;
}

View the entire Raku script for this task on GitHub.

Perl

In Perl, we need to use the quotemeta function (or \Q within the regular expression, like I did) to escape any metacharacters in the separator (which both . and $ are). And because the split doesn’t have a parameter to skip empty results, we have to filter them out by grepping out strings that have match a regular expression anchored at the beginning and end with no characters in-between.

sub splitOnSeparator($separator, @words) {
  my @output;
  foreach my $str ( @words ) {
    push @output, grep { !/^$/ } split(/\Q$separator/, $str);
  }
  return @output;
}

View the entire Perl script for this task on GitHub.

Python

I keep forgetting about Python’s extend method on lists.

def splitOnSeparator(words, separator):
    output = []
    for str in words:
        output.extend(
            list(filter(lambda w: w>"", str.split(separator)))
        )
    return output

View the entire Python script for this task on GitHub.


Task 2: Weakest Row

You are given an m x n binary matrix i.e. only 0 and 1 where 1 always appear before 0.

A row i is weaker than a row j if one of the following is true:

a) The number of 1s in row i is less than the number of 1s in row j.
b) Both rows have the same number of 1 and i < j.

Write a script to return the order of rows from weakest to strongest.

Example 1

Input: $matrix = [
                   [1, 1, 0, 0, 0],
                   [1, 1, 1, 1, 0],
                   [1, 0, 0, 0, 0],
                   [1, 1, 0, 0, 0],
                   [1, 1, 1, 1, 1]
                 ]
Output: (2, 0, 3, 1, 4)

The number of 1s in each row is:
- Row 0: 2
- Row 1: 4
- Row 2: 1
- Row 3: 2
- Row 4: 5

Example 2

Input: $matrix = [
                   [1, 0, 0, 0],
                   [1, 1, 1, 1],
                   [1, 0, 0, 0],
                   [1, 0, 0, 0]
                 ]
Output: (0, 2, 3, 1)

The number of 1s in each row is:
- Row 0: 1
- Row 1: 4
- Row 2: 1
- Row 3: 1

Approach

This feels like a map then sort: loop over the matrix, counting the 1s in each row (though, because it’s defined to be just 1s and 0s, we can just sum the row). Then do a sort of the indices based on that count. In fact, we’ve done a sort like this before, in the Most Frequent Letter Pair task for PWC 247.

Raku

I’m borrowing my matrix printing code from PWC 248. Once again, I have to shout out to the Raku List method .end.

sub weakestRows(@matrix) {
  my @oneCount = @matrix.map({ $_.sum });
  my @weakest = (0 .. @oneCount.end).sort: {
    # sort by count first
    @oneCount[$^a] <=> @oneCount[$^b]
    ||
    # then by index order
    $^a <=> $^b
  };

  return @weakest;
}

View the entire Raku script for this task on GitHub.

Perl

Just the usual Raku-to-Perl changes.

use List::Util qw( sum );

sub weakestRows(@matrix) {
  my @oneCount = map { sum(@$_) } @matrix;
  my @weakest = sort {
    # sort by count first
    $oneCount[$a] <=> $oneCount[$b]
    ||
    # then by index order
    $a cmp $b
  } (0 .. $#oneCount);

  return @weakest;
}

View the entire Perl script for this task on GitHub.

Python

And, just like I did back in PWC 247, I’d like to point out that Python’s Decorate-Sort-Undecorate idiom is really just a Schwartzian Transformation.

def weakestRows(matrix):
    oneCount = [ sum(row) for row in matrix ]
    # sort the rows by their oneCount values
    # use the Decorate-Sort-Undecorate idiom
    # to convert the dict into a list
    # https://docs.python.org/3/howto/sorting.html#decorate-sort-undecorate
    decorated = [
        (oneCount[i], i) for i in range(len(oneCount))
    ]
    sorted_tuples = sorted(
        decorated,
        # the - before the first element sorts descending
        key=lambda k: (k[0], k[1])
    )
    weakest = [ t[1] for t in sorted_tuples ]
    return weakest

View the entire Python script for this task on GitHub.


Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-253/packy-anderson

One thought on “Perl Weekly Challenge: The Weakest Split

  1. Pingback: Perl Weekly Challenge: Odd Char Seems to be the Most Frequent Word | Packy’s Place

Comments are closed.