Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rearrange a number into non-English alphabetical order in Raku

Tags:

hash

raku

I'm trying to rearrange a number into non-English alphabetical order.

my @numDE = < null eins zwei drei  vier fünf sechs sieben acht  neun >;
# english   < zero one  two  three four five six   seven  eight nine >;

my %numrank; # The lookup hash: number => its_alphabetical_order

for @numDE.sort.kv -> $k,$v {%numrank{%(@numDE.kv.reverse){"$v"}} = "$k"};

my %temp; # number => reassigned order value  
%temp{"$_"}= %numrank{"$_"} for "2378".comb; # 2378 sample input
say %temp.sort(*.values); # this prints:
# (8 => 0 3 => 1 7 => 7 2 => 9) it's in order but unformatted

my %target= %temp.sort(*.values); # Attempt to print it in order
for %target.kv -> $k,$v {print $k}; # that prints in random order

1- How to print the %temp hash in order?

2- (optional) Is there a way to start using the hash variable without declaring it with my i.e. as it is in Python?

3- (optional) I've chosen the lookup approach to solve this problem and the resulting code looks a bit convoluted. Could the solution be made shorter using any other approaches?

like image 218
Lars Malmsteen Avatar asked Sep 13 '20 18:09

Lars Malmsteen


1 Answers

So there are a couple of things that I'd take a look at, but it does look like you're doing a few extra steps (in the comments you said you've tried a few different things, so I imagine you didn't initial do some many extra).

You've first given yourself the german numbers:

my @numDE = <null eins zwei drei vier fünf sechs sieben acht neun>;

Next, you want to be able to order things based on the German spelling. I'll try to use the method you've approached, but I'll show you a simplier way at the end. Your next step is to effectively cache the sort order of them, and store it into a variable "numrank". Building off of @numDE.sort.kv, we get

my %numrank;
for @numDE.sort.kv -> $k, $v {
    %numrank{$v} = $k;
}
say %numrank;
# {acht => 0, drei => 1, eins => 2, fünf => 3, neun => 4, null => 5, sechs => 6, sieben => 7, vier => 8, zwei => 9}

Okay, not bad. Also note that while the output of %numrank appears to be ordered, as a hash, it is inherently unordered. It just happens to print keys in alphabetical as a rule and our keys and values are sorted on those lines. Now we just need to use the actual number as the key, rather than the German name instead.

my %numrank;
for @numDE.sort.kv -> $k, $v {
    my $id == @numDE.first: $v;
    %numrank{$id} = $k;
}
say %numrank;

Oops, we get the same thing. This is because .first returns the actually object. For its index, we just attach the :k adverb:

my %numrank;
for @numDE.sort.kv -> $k, $v {
    my $id == @numDE.first: $v, :k;
    %numrank{$id} = $k;
}
say %numrank;
# {0 => 5, 1 => 2, 2 => 9, 3 => 1, 4 => 8, 5 => 3, 6 => 6, 7 => 7, 8 => 0, 9 => 4}

Perfect, now we can see that the value for 8 (acht) is 0 as it's first, and for 2 (zwei) is 9, as it's last. Note that we could also have used an array here, since our indices are numbers (using @numrank and then doing @numrank[$id] = $k)

Now to sort some stuff. In your code you have

%temp{"$_"}= %numrank{"$_"} for "2378".comb; # 2378 sample input

This creates an unordered hash, wherein the name of each key is a digit, and its value is its rank. That's basically what we had above in the first attempt at making %numrank. But because %temp is a hash, if you have any two digits that repeat, you'll lose the extras:

%temp{"$_"}= %numrank{"$_"} for "222".comb;
# {2 => 9} 

Instead, I think you want to create an array which can allow for ordering:

my @temp = ($_ => %numrank{"$_"}) for "22378".comb;
# ^^ both 2s are preserved

Now you can simply sort on the values:

say @temp.sort: *.values;

You could loop directly on this:

for @temp.sort(*.values) {
   print .key;
}

The simpler way

"2378".comb.sort: { @numDE[$^digit] }
# (8 3 7 2) # acht drei seiben zwei 

Here we sort the combed digits based on the German text form of each number. @numDE as the names of the numbers, and $^digit is an implicit variable that holds the digit (the [ ] automatically coerces it to a number for us). If you plan on using it regularly, you can actually store the block in a variable, like this:

my &sort-de = sub ($digit) { @numDE[$digit] };
"87446229".comb.sort: &sort-de;
# (8 9 6 7 4 4 2 2)

And as mentioned above, you can do the for loop directly on this, if you want to style it in some other manner:

for "87446229".comb.sort(&sort-de) {
   say $_
}
like image 131
user0721090601 Avatar answered Jan 04 '23 14:01

user0721090601