Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re: Challenge: sort weekdays in week-order (elegantly and efficiently)

by AnomalousMonk (Archbishop)
on Jul 25, 2022 at 02:19 UTC ( [id://11145698]=note: print w/replies, xml ) Need Help??


in reply to Challenge: sort weekdays in week-order (elegantly and efficiently)

Another approach. Not necessarily better, but anyway... An %order hash is still needed to encapsulate day order. No modules or regex matching involved. (The code is presented in a very simple module for convenience of testing and presentation, but module encapsulation is not essential.) Day order is Sunday - Saturday and can only be changed by changing the code. Encoding of day order value is 0 .. 6 per the C tm structure tm_wday element.

This approach depends on the fact that "short" weekday names like 'mon' are all present as left-anchored substrings of the "long" names, e.g., 'monday'. Unfortunately, this leads to false-positives such as 's' matching Sunday and 'mond' matching Monday. OTOH, variations like th/thu/thur/thurs/thursday all match correctly.

File CmpDay.pm:

# CmpDay.pm compare weekday names, find name order 24 +jul22waw package CmpDay; use strict; use warnings; # use Data::Dump qw(dd); # for debug # prepare weekdays string for weekday substring indexing. # # this trick depends on the fact that each "short" day name # is a left-anchored substring of the corresponding "long" name. # this allows recognition of some wider variations in day names, # e.g., tu/tue/tues for tuesday and th/thu/thur/thurs for thursday. # # unfortunately, it also allows some false positives like 'm' or 'mond +' # for monday (which may or may not be a false positive in your # application - YMMV). # # this trick also works for month names, e.g., jan/january, etc. # long day names in canonical form (e.g., case) and in week-order # per the C tm struct. my @long_names = qw(sunday monday tuesday wednesday thursday friday sa +turday); # this character or multi-character sequence is used as the left-ancho +r # of day names for searching via the index() built-in. # this character or multi-character sequence must not appear in any na +me. my $left_anchor = "\n"; # must not appear in any name sub canonicalize_name { return "$left_anchor\L$_[0]"; } # string for short/long name substring indexing my $wd = join '', map canonicalize_name($_), '', @long_names; # ^ # | # extra empty string in first position -+ # allows day_to_i() to evaluate to + # undef for an empty string 'name' -+ # dd '$wd', $wd; # for debug # per struct tm: tm_wday, days since Sunday, 0-6 my %day_indices = map { index($wd, canonicalize_name($long_names[$_])) => $_ } 0 .. $#long_names ; # dd '%day_indices', \%day_indices; # for debug sub day_to_i { return $day_indices{index $wd, canonicalize_name($_[0]) +}; } sub i_to_day { return $long_names[$_[0]]; } 1;
File CmpDay.t:
# CmpDay.t compare weekday names, find name order 24 +jul22waw use strict; use warnings; use Test::More; use Test::NoWarnings; use List::Util qw(shuffle sum); use Data::Dump qw(dd pp); note "\n=== testing under perl version $] ===\n\n"; use lib '.'; BEGIN { use_ok 'CmpDay'; } # test name to wday number conversion. my @Test_wday_to_n = ( 'invalid weekday names (no wday numbers)', [ undef, '', qw(x xyz xsun sunx xsunx mondaytuesday mondayxtuesday + sunsun) ], 'valid weekday names -> wday numbers', [ 0, qw(su sun sunday sU Su SU sUnDaY) ], [ 3, qw(we wed wednesday wE We WE WednEsDaY) ], [ 6, qw(sa sat saturday sA Sa SA sAtUrDaY) ], ); # end @Test_wday_to_n # test wday number to name conversion. my @Test_n_to_wday = ( 'invalid weekday numbers (no corresponding wday name)', [ 7, undef ], [ -8, undef ], 'valid weekday numbers -> wday names', [ 0, 'sunday' ], [ -7, 'sunday' ], [ 3, 'wednesday' ], [ -4, 'wednesday' ], [ 6, 'saturday' ], [ -1, 'saturday' ], ); # end @Test_n_to_wday my @additional = qw(use_ok Test::NoWarnings @unsorted_days); plan 'tests' => (sum map $#$_, grep { ref eq 'ARRAY' } @Test_wday_to_n +) + (sum map $#$_, grep { ref eq 'ARRAY' } @Test_n_to_wday +) + @additional ; VECTOR: for my $ar_vector (@Test_wday_to_n) { if (not ref $ar_vector) { note $ar_vector; next VECTOR; } my ($expected, @days) = @$ar_vector; for my $day (@days) { my $wday = CmpDay::day_to_i($day); is $wday, $expected, "'$day' -> " . pp $wday; } } # end for VECTOR VECTOR: for my $ar_vector (@Test_n_to_wday) { if (not ref $ar_vector) { note $ar_vector; next VECTOR; } my ($wday_n, $expected_day) = @$ar_vector; my $got_day = CmpDay::i_to_day($wday_n); is $got_day, $expected_day, "$wday_n -> " . pp $expected_day; } # end for VECTOR note "\ntest sorting of mixed short/long day names\n\n"; # define standard and variant short/long weekday names for testing. my @days = qw( mo mon monday tu tue tues tuesday we wed wednesday th thu thur thurs thursday fr fri friday sa sat saturday su sun sunday ); # prepare randomized weekday list with uc/lc variations for sort testi +ng. my @unsorted_days = shuffle @days, map(ucfirst, @days), map(uc, @days), qw(mO mOn tU TuE tUeS wEdNeSdAy tH tHuR ThUr ThurS tHuRs sA sU suN +), ; # # works # # sort by day-name order (ascending) then lexicographic (ascending) +by day-name. # my @sorted_days = # map $_->[0], # sort { # $a->[1] <=> $b->[1] # sort by day order ascending # or # $a->[0] cmp $b->[0] # then lexicographic ascending # } # map [ $_, defined CmpDay::day_to_i($_) ? CmpDay::day_to_i($_) : +die "bad day '$_'" ], # @unsorted_days # ; # works # sort by day-name order (ascending) then lexicographic (ascending) by + day-name. my @sorted_days = map unpack('x[C] a*', $_), sort map pack('C a*', CmpDay::day_to_i($_), $_), map { defined CmpDay::day_to_i($_) ? $_ : die "bad day '$_'" } @unsorted_days ; # dd \@sorted_days; # for debug is_deeply \@sorted_days, [ qw( SU SUN SUNDAY Su Sun Sunday sU su suN sun sunday MO MON MONDAY Mo Mon Monday mO mOn mo mon monday TU TUE TUES TUESDAY Tu TuE Tue Tues Tuesday tU tUeS tu tue tues tuesday WE WED WEDNESDAY We Wed Wednesday wEdNeSdAy we wed wednesd +ay TH THU THUR THURS THURSDAY Th ThUr Thu Thur ThurS Thurs Thursday tH tHuR tHuRs th thu thur thurs thursday FR FRI FRIDAY Fr Fri Friday fr fri friday SA SAT SATURDAY Sa Sat Saturday sA sa sat saturday )], 'shuffled day names'; note "\n=== testing done ===\n\n";
Output:
Win8 Strawberry 5.8.9.5 (32) Sun 07/24/2022 21:31:02 C:\@Work\Perl\monks\bliako >perl CmpDay.t ok 1 - use CmpDay; # # === testing under perl version 5.008009 === # 1..41 # invalid weekday names (no wday numbers) ok 2 - '' -> undef ok 3 - 'x' -> undef ok 4 - 'xyz' -> undef ok 5 - 'xsun' -> undef ok 6 - 'sunx' -> undef ok 7 - 'xsunx' -> undef ok 8 - 'mondaytuesday' -> undef ok 9 - 'mondayxtuesday' -> undef ok 10 - 'sunsun' -> undef # valid weekday names -> wday numbers ok 11 - 'su' -> 0 ok 12 - 'sun' -> 0 ok 13 - 'sunday' -> 0 ok 14 - 'sU' -> 0 ok 15 - 'Su' -> 0 ok 16 - 'SU' -> 0 ok 17 - 'sUnDaY' -> 0 ok 18 - 'we' -> 3 ok 19 - 'wed' -> 3 ok 20 - 'wednesday' -> 3 ok 21 - 'wE' -> 3 ok 22 - 'We' -> 3 ok 23 - 'WE' -> 3 ok 24 - 'WednEsDaY' -> 3 ok 25 - 'sa' -> 6 ok 26 - 'sat' -> 6 ok 27 - 'saturday' -> 6 ok 28 - 'sA' -> 6 ok 29 - 'Sa' -> 6 ok 30 - 'SA' -> 6 ok 31 - 'sAtUrDaY' -> 6 # invalid weekday numbers (no corresponding wday name) ok 32 - 7 -> undef ok 33 - -8 -> undef # valid weekday numbers -> wday names ok 34 - 0 -> "sunday" ok 35 - -7 -> "sunday" ok 36 - 3 -> "wednesday" ok 37 - -4 -> "wednesday" ok 38 - 6 -> "saturday" ok 39 - -1 -> "saturday" # # test sorting of mixed short/long day names # ok 40 - shuffled day names # # === testing done === # ok 41 - no warnings


Give a man a fish:  <%-{-{-{-<

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11145698]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (6)
As of 2024-04-24 07:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found