http://qs321.pair.com?node_id=1192551


in reply to Sort undef

I think you can define your own sorting sub cmp_undef.

Something like

sub cmp_undef { my ( $a, $b ) = @_; return $a cmp $b if defined $a and defined $b; return $b cmp $a; # invert order otherwise } @$ResultsFinal = sort \&cmp_undef(...,...), @$ResultsFinal;

Untested!

Update:

wait this might go wrong if cmp_undef("",undef) returns 0. (Not sure)

So you'd need to treat the 3 extra cases for $a, $b being undef and return -1,0,1 accordingly.

Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
Je suis Charlie!

Replies are listed 'Best First'.
Re^2: Sort undef
by SuicideJunkie (Vicar) on Jun 12, 2017 at 16:19 UTC

    This being a simple case, I thought the typical way to do it would be in the form:

    sort { (defined($a) <=> defined($b)) || ($a cmp $b) }

      Nice! I was worried that <=> can't handle "" without warnings, but that's not the case here. :)

      (empty string is the false value returned from defined(undef) )

      EDIT

      Forget it, I'm getting old ... false is a dual var which resolves to 0 in numeric context.

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

Re^2: Sort undef
by LanX (Saint) on Jun 12, 2017 at 13:35 UTC
    > wait this might go wrong if cmp_undef("",undef) returns 0. (Not sure)

    actually it works undef is not cast to "" and hence less than empty string°

    but it seems you didn't sue strictures, that's why I needed to add no warnings 'uninitialized';

    > So you'd need to treat the 3 extra cases for $a, $b being undef and return -1,0,1 accordingly.

    see cmp_undef2 for that solution

    use strict; use warnings; use Data::Dump; sub cmp_undef1 { return $a cmp $b if defined $a and defined $b; no warnings 'uninitialized'; return $b cmp $a; # invert order otherwise } sub cmp_undef2 { if (defined $a) { if (defined $b) { $a cmp $b; } else { -1 } } else { if (defined $b) { 1 } else { 0 } } } my @array =( (undef)x 3, qw/c b a/, ("")x3); my @result = sort cmp_undef1 @array; dd \@result; @result = sort cmp_undef2 @array; dd \@result;

    output

    ["", "", "", "a", "b", "c", undef, undef, undef] ["", "", "", "a", "b", "c", undef, undef, undef]

    I don't want to elaborate on the sort SUBNAME LIST syntax see sort for details and other variations. (TIMTOWTDI I chose the shortest)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

    °) which is strange because

    DB<1> p "" eq undef 1
      > actually it works undef is not cast to "" and hence less than empty string

      No, it works because sort is stable and you declared the lucky case to be the input. Try again with

      use List::Util qw{ shuffle }; my @array = shuffle((undef) x 3, qw/c b a/, ("") x 3);

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
        Ah, thanks! :)

        > you declared the lucky case to be the input

        In my defence:

        To be sure I also tried

         my @array =  ( ("")x3, (undef)x 3, qw/c b a/, ("")x3, (undef) x3 );

        which still gives for cmp_undef1

        [ "", "", "", "", "", "", "a", "b", "c", undef, undef, undef, undef, undef, undef, ]

        which seems to be another "lucky case".

        I don't know how "stable" is defined but it turns out to be not that easy to construct a counterexample without shuffle.

        my @array =  ( undef, "", qw/c b a/, "", undef  );

        ->

        ["", "a", "b", "c", undef, "", undef]

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

      Another slight variation on a custom sort sub:

      c:\@Work\Perl\monks>perl -le "use warnings; use strict; ;; use Test::More 'no_plan'; use Test::NoWarnings; ;; my $OptOrd = 1; ;; my $Test = [ [ 1, 'c' ], [ 2, 'a' ], [ 3, '' ], [ 4, undef ], [ 5, 'foo' ], [ 6, '' ], [ 7, 'cee' ], [ 8, undef ], [ 9, 'tee' ], [ 10, 't' ], ]; ;; my $Expected = [ [ 3, '' ], [ 6, '' ], [ 2, 'a' ], [ 1, 'c' ], [ 7, 'cee' ], [ 5, 'foo' ], [ 10, 't' ], [ 9, 'tee' ], [ 4, undef ], [ 8, undef ], ]; ;; my @got = sort by_deaccented_ascending_with_undef_highest @$Test; ;; is_deeply \@got, $Expected, 'custom sort sub'; ;; done_testing; ;; sub deaccent { return $_[0]; } ;; sub by_deaccented_ascending_with_undef_highest { my ($aa, $bb) = ($a->[$OptOrd], $b->[$OptOrd]); ;; return defined $aa && defined $bb ? deaccent($aa) cmp deaccent($bb) : defined $bb cmp defined $aa ; } " ok 1 - custom sort sub 1..1 ok 2 - no warnings 1..2
      defined returns '' (empty string) for undefined, 1 for defined, and '' will lexically cmp below 1.


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