Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

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

by bliako (Monsignor)
on Jul 21, 2022 at 14:08 UTC ( [id://11145633]=CUFP: print w/replies, xml ) Need Help??

It just occured to me that I do not know how to sort weekdays in week-order except with this:

my @weekdays = qw/Monday Saturday Thursday/; my %order = ( monday => 1, tuesday => 2, wednesday => 3, thursday => 4, friday => 5, saturday => 6, sunday => 7, ); print join ",", map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, $order{lc $_}] } @weekdays;

is there a way without using that %order?

bw, bliako

Replies are listed 'Best First'.
Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by hippo (Bishop) on Jul 21, 2022 at 16:47 UTC

    Coming directly to you from the Horrible-But-Works department is this monstrosity:

    #!/usr/bin/env perl use strict; use warnings; use Digest::SHA 'sha512256_hex'; my @weekdays = qw/Monday Saturday Thursday/; print join ",", map { $_->[0] } sort { $a->[1] cmp $b->[1] } map { [$_, substr (sha512256_hex (substr (lc ($_), 1, 7)), 26, 1)] } @ +weekdays;

    🦛

Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by choroba (Cardinal) on Jul 22, 2022 at 09:50 UTC
    This still uses the %order, but populates it via a core module:
    use Time::Piece; my %order = map { split ' ', 'Time::Piece'->strptime("$_", "%d")->strftime('%A %u') } 1 .. 7;
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      bravo choroba!

      I was trying something similar but I easily get annoyed by strptime et similia...

      Infact even with your code I hit Error parsing time at C:/perl5.26.64bit/perl/lib/Time/Piece.pm line 581.

      I'd modify your code to be even more stable, in the case they accept my proposal for 8 days week, adding Lokiday as jolly day free for all :D

      my %order = map { split ' ', 'Time::Piece'->strptime("$_", "%d")->strftime('%A %u') } Time::Piece::day_list();

      bliako: your solution is already elegant, readable and perlish: dont overcomplicate :)

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
        > even with your code I hit Error parsing time at C:/perl5.26.64bit/perl/lib/Time/Piece.pm line 581.

        That's weird, I'm running it without errors on 5.26.1 (Time::Piece 1.31).

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      FWIW
      > perl -de0 DB<38> use Time::Piece DB<39> x Time::Piece::day_list 0 'Sun' 1 'Mon' 2 'Tue' 3 'Wed' 4 'Thu' 5 'Fri' 6 'Sat' DB<40> x @{Time::Piece::_get_localization->{weekday}} 0 'Sunday' 1 'Monday' 2 'Tuesday' 3 'Wednesday' 4 'Thursday' 5 'Friday' 6 'Saturday'

      And if you don't like the traditional order

      DB<41> @W = @{Time::Piece::_get_localization->{weekday}} DB<42> push @W, shift @W DB<43> x @W 0 'Monday' 1 'Tuesday' 2 'Wednesday' 3 'Thursday' 4 'Friday' 5 'Saturday' 6 'Sunday' DB<44>

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        Hmm... Not present in 5.26.1, works but not documented in 5.37.1. I'm not sure I'd use it.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by tybalt89 (Monsignor) on Jul 21, 2022 at 15:49 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11145633 use warnings; use List::AllUtils qw( nsort_by ); my @weekdays = qw/Monday Saturday Thursday/; print "$_\n" for nsort_by { 'motuwethfrsasu' =~ substr lc, 0, 2; $-[0] } @weekdays;

    Outputs:

    Monday Thursday Saturday
Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by haukex (Archbishop) on Jul 22, 2022 at 14:10 UTC

    I agree with the others that using a module like Time::Piece, DateTime, or at least Sort::Key is the right way to go about this. But just for fun, TIMTWOTDI, and to fulfill your "some kind of transform" idea:

    use List::Util qw/shuffle/; my @weekdays = shuffle qw/ Monday Tuesday Wednesday Thursday Friday Saturday Sunday /; say join ", ", map { $$_[0] } sort { $$a[1] cmp $$b[1] } map { [$_, lc(substr $_,0,2)=~tr/softwarehum/50411307860/r] } @weekdays;

    Update: Anagram'd

      Inspired by above post.

      #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11145633 use warnings; use List::AllUtils qw( shuffle sort_by ); my @weekdays = shuffle qw/Monday tuesday wednesday friday sunday Saturday Thursday/; print "@{[ sort_by { lc =~ tr/muwhft/a-e/dr } @weekdays ]}\n";
        ++tybalt89!! That was a very clever use of "tr" and its options. I added a small tweak using unshift and pop to put Sunday first.

        #!/usr/bin/perl use strict; use warnings; use List::AllUtils qw( shuffle sort_by ); my @weekdays = shuffle qw/Monday Tuesday Wednesday Friday Sunday Satur +day Thursday/; @weekdays = @{[ sort_by { lc =~ tr/muwhft/a-e/dr } @weekdays ]}; unshift(@weekdays,pop(@weekdays)); print "@weekdays\n";

        "It's not how hard you work, it's how much you get done."

Re: Challenge: sort weekdays in week-order (Sort::Key)
by LanX (Saint) on Jul 21, 2022 at 15:25 UTC
    Using Salva's Sort::Key is efficient and elegant, but I can't get rid of the order info
    use v5.12; use warnings; use Sort::Key qw/ikeysort/; use Data::Dump qw/pp dd/; my $n; my %order = map {$_ => ++$n} qw/monday tuesday wednesday thursday friday saturday sunday/; pp ikeysort { $order{lc($_)} } qw/Monday Saturday Thursday/;

    ==>

    ("Monday", "Thursday", "Saturday")

    edit
    oops wait, there is a bug...

    update

    Fixed bug in %order creation

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

Re: Challenge: sort weekdays in week-order (grep)
by LanX (Saint) on Jul 21, 2022 at 15:08 UTC
    maybe?

    use v5.12; use warnings; use Data::Dump qw/pp dd/; my $input = join "|", qw/Monday Saturday Thursday/; my @order = qw/monday tuesday wednesday thursday friday saturday sunda +y/; pp grep { /^($input)$/i } @order;

    ==>

    ("monday", "thursday", "saturday")

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      Similarly,
      #!/usr/bin/perl -l use warnings; use strict; my $input = join "|", qw/Monday Saturday Thursday/; "monday tuesday wednesday thursday friday saturday sunday" =~ m/\b($in +put)\b(?{ print "[$1]" })(*FAIL)/i; print join ' ', grep s/,//, split ' ', "monday tuesday wednesday thurs +day friday saturday sunday" =~ s/\b($input)\b\K/,/igr;
      [monday] [thursday] [saturday] monday thursday saturday
        cool! :)

        hm theoretically it should be possible to swap it and generate a (complicated) regex from "monday .. sunday" which sorts the input without explicit sort like the others showed.

        But I'm better opting out now! =)

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by LanX (Saint) on Jul 21, 2022 at 14:27 UTC
    > is there a way without using that %order?

    ehm ... you need this order information somehow.

    So is your question

    • if this order could be taken from some Date or Time module like DateTime
    • or if there is some obfuscated way to calculate it like with the Doomsday algorithm
    ?

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    Side note: The week starts traditionally on Sunday. At least for the church.

      The latter: some kind of algorithm or rather a transform of each weekday into a new name which is alphabetically sorted right (as per the %order or whatever calendar order). For example, transform each weekday by removing the 1st letter, then their cmp order is correct. OK this does not work. But I was looking for this kind of transform. Of course I can try exhaustively find such transform with random masks on the weekday bytes.

      monday:011011010110111101101110011001000110000101111001 tuesday:01110100011101010110010101110011011001000110000101111001 wednesday:011101110110010101100100011011100110010101110011011001000110 +000101111001 thursday:0111010001101000011101010111001001110011011001000110000101111 +001 friday:011001100111001001101001011001000110000101111001 saturday:0111001101100001011101000111010101110010011001000110000101111 +001 sunday:011100110111010101101110011001000110000101111001

      (with this:

      for (qw/monday tuesday wednesday thursday friday saturday sunday/){ print $_ .':'. join('', map { sprintf "%08b", ord($_) } split(//, lc +$_))."\n"; }
      )

        Honestly, I find those magical formulas° always too voodoo.

        From a maintenance point of view they are horrible, because they don't scale well with new requirements.

        Even Doomsday fails if you go back before Gregorian calendar

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

        °) don't know the correct term here... ³

        update

        there is only a limited number of orders 7!

        so you could try a brute force search composing formulas based on inputs like substrings or word-length.

        I'm still not sure if the solution will be shorter than just a clever list.²

        update

        ²) tybaldt's solution with "motuwethfrsasu" is just an example of such a clever list.

        ³) kind of golfing, isn't it?

Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by AnomalousMonk (Archbishop) on Jul 25, 2022 at 02:19 UTC

    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:

    File CmpDay.t: 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:  <%-{-{-{-<

Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by kcott (Archbishop) on Jul 26, 2022 at 14:05 UTC

    G'day bliako,

    Here's another way: enum for the order; and, an Orcish Manoeuvre for efficiency.

    #!/usr/bin/env perl use 5.010; use strict; use warnings; use enum qw{sun mon tue wed thu fri sat}; say '*** Check Orcish Manoeuvre is working ***'; say _canon_day_test($_) for qw{Mon mon Monday monday} x2; say '*** Your sort example ***'; my @weekdays = qw/Monday Saturday Thursday/; say for sort { _canon_day($a) <=> _canon_day($b) } @weekdays; say '*** More complex example ***'; my @many_form_weekdays = qw{ thurs tue Thu Tues Thursday tuesday Fri Sun sat Fri Sun sat SATURDAY FRIDAY SUNDAY Wednesday Mon wed monday sun Sat friday Friday Sunday }; say for sort { _canon_day($a) <=> _canon_day($b) } @many_form_weekdays +; sub _canon_day { my ($day) = @_; state $day_for = {}; return $day_for->{$day} ||= eval lc substr $day, 0, 3; } sub _canon_day_test { my ($day) = @_; state $day_for = {}; say "'$day' needs Orc" unless exists $day_for->{$day}; return $day_for->{$day} ||= eval lc substr $day, 0, 3; }

    _canon_day_test() is identical to _canon_day() except for the say statement: it's just for testing.

    You may want to add some validation code, but that wasn't part of your question.

    Output:

    *** Check Orcish Manoeuvre is working *** 'Mon' needs Orc 1 'mon' needs Orc 1 'Monday' needs Orc 1 'monday' needs Orc 1 1 1 1 1 *** Your sort example *** Monday Thursday Saturday *** More complex example *** Sun Sun SUNDAY sun Sunday Mon monday tue Tues tuesday Wednesday wed thurs Thu Thursday Fri Fri FRIDAY friday Friday sat sat SATURDAY Sat

    — Ken

      I have trouble finding a canonical explanation of Orcish Manoeuvre, but isn't Memoize faster in this case?

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        G'day Rolf,

        "I have trouble finding a canonical explanation of Orcish Manoeuvre, ..."

        "A Fresh Look at Efficient Perl Sorting" describes many sorting techniques. The section on OM points to a footnote with details of a book by the inventor of this algorithm. I don't own, nor have I read, this book, so I can't comment further.

        "... but isn't Memoize faster in this case?"

        Try Benchmark to answer that. Please post your results: I, and no doubt others, would be interested.

        — Ken

        OM is basically ST, but where the sort is provided the default comparison function ($a cmp $b).

        Technically, any of the four builtin compare functions would work equally fast.

        • $a cmp $b
        • $b cmp $a
        • $a <=> $b
        • $b <=> $a
Re: Challenge: sort weekdays in week-order (elegantly and efficiently)
by ikegami (Patriarch) on Jul 26, 2022 at 14:15 UTC

    Same approach you took, but clearer and faster:

    use Sort::Key qw( ikeysort ); my @sorted = ikeysort { $order{ fc( $_ ) } } @unsorted;

        yup, overlooked that one.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (5)
As of 2024-04-23 07:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found