# More Weight for Examples! **Challenge 279 solutions in Perl by Matthias Muth** ## Task 1: Sort Letters > You are given two arrays, @letters and @weights.
> Write a script to sort the given array @letters based on the @weights.
>
> Example 1
> Input: @letters = ('R', 'E', 'P', 'L')
> @weights = (3, 2, 1, 4)
> Output: PERL
>
> Example 2
> Input: @letters = ('A', 'U', 'R', 'K')
> @weights = (2, 4, 1, 3)
> Output: RAKU
>
> Example 3
> Input: @letters = ('O', 'H', 'Y', 'N', 'P', 'T')
> @weights = (5, 4, 2, 6, 1, 3)
> Output: PYTHON
Probably the most efficient way to solve this task is *not* to use sort, but to directly assign letters to their positions in the result string. This works for all the examples, because in these examples, the `@weights` are very regular: - they can be mapped one-by-one to result positions, - no weight appears twice, - the weights cover all positions completely. Actually, at least for the examples, `@weights` should be called `@positions` (minding that they are one-based, not zero-based). So here's the simple version, to cover the examples.
We need to initialize the result string to have the correct length, because we will assign letters to positions in random order. ```perl use v5.36; sub sort_letters_1( $letters, $weights ) { my $result = " " x $letters->@*; substr $result, $weights->[$_] - 1, 1, $letters->[$_] for 0..$letters->$#*; return $result; } ``` **What if the weights were not as regular as they are in the examples?** We should consider cases where weights do *not* map one-by-one to positions.
Let's put some 'heavier weights' in, and consider this additional example: > Example 4
> Input: @letters = ('R', 'E', 'P', 'L')
> @weights = (3333, 2222, 1111, 3333)
> Output: PERL
Here: * the lowest weight is not 1, * weights are not consecutive (they contain gaps), * same weight values are used more than once for different letters
(behavior is not defined in this case, but we should do something useful), * the weight values are not necessarily small integers
(which can cause memory problems when we incautiously map weight values to string positions or array indexes). So what do we do? My ideas are these: * Use the weight values as hash keys instead of string positions or array indexes to store where any letter is going to be put.
This addresses both the 'not consecutive' and the 'no small integers' issues. We can have a weight of 6548632 without running out of bounds as we would with a string or an array. * Store a *list* of letters with each 'weight' hash key.
This deals with the case of multiple letters having the same weight value. Every hash entry will contain an array-ref to a list of all letters having that weight. I'm also happy to highlight the `for_list` Perl feature that was added in Perl 5.36 for iterating over multiple values at a time. Especially its use together with the `mesh` function from `List::Util` makes some things simple and nice.
Where normally I would have to iterate over `0..$#array`, I can avoid this here, as well as the use of `$_` in the loop.
Certainly less 'perlish', but easy for the eyes! To get the result string from the hash, we sort the hash keys (numerically!) and concatenate all letters from their entries in order.
Like this: ```perl use v5.36; no warnings 'experimental::for_list'; use List::Util qw( mesh ); sub sort_letters( $letters, $weights ) { my %buckets; for my ( $letter, $weight ) ( mesh $letters, $weights ) { push $buckets{$weight}->@*, $letter; } return join "", map $buckets{$_}->@*, sort { $a <=> $b } keys %buckets; } ``` ## Task 2: Split String > You are given a string, \$str.
> Write a script to split the given string into two containing exactly same number of vowels and return true if you can otherwise false.
>
> Example 1
> Input: \$str = "perl"
> Ouput: false
>
> Example 2
> Input: \$str = "book"
> Ouput: true
> Two possible strings "bo" and "ok" containing exactly one vowel each.
>
> Example 3
> Input: \$str = "good morning"
> Ouput: true
> Two possible strings "good " and "morning" containing two vowels each or "good m" and "orning" containing two vowels each.
Actually the task assignment 'split the given string into two containing exactly same number of vowels' can be completely ignored. The result is never used. It's like a piece of 'dead code' that never gets executed and that can be removed. The only information we need to return is whether *we can* split the string into two such pieces! We can do so if and only if we can split up the string's *vowels* into two equal pieces.
Which means we need an even number of vowels. Ok, then let's count the vowels! And the rest is easy... ```perl use v5.36; sub split_string( $str ) { my @vowels = $str =~ /[aeiou]/ig; return scalar @vowels % 2 == 0; } ``` #### **Thank you for the challenge!**