Re: Sort undef
by Discipulus (Canon) on Jun 11, 2017 at 23:19 UTC
|
If sort works like this you can modify the returned array:
push @$ResultsFinal, shift @$ResultsFinal;
If have many of them you can also push @$ResultsFinal, shift @$ResultsFinal until $$ResultsFinal[0];
PS or in the case undef has to be discarded you can @$ResultsFinal = ( sort { ... } grep {defined} @$ResultsFinal );
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.
| [reply] [d/l] [select] |
Re: Sort undef
by LanX (Saint) on Jun 12, 2017 at 00:04 UTC
|
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.
| [reply] [d/l] [select] |
|
sort
{
(defined($a) <=> defined($b))
||
($a cmp $b)
}
| [reply] [d/l] |
|
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.
| [reply] [d/l] |
|
> 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)
°) which is strange because
DB<1> p "" eq undef
1
| [reply] [d/l] [select] |
|
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,
| [reply] [d/l] [select] |
|
|
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: <%-{-{-{-<
| [reply] [d/l] [select] |
Re: Sort undef
by KurtZ (Friar) on Jun 11, 2017 at 22:38 UTC
|
Well deaccent() must handle undef differently. Like returning a string guarantied to be always maximal
.
Like 'X'x100
100 is a guess, you don't show us your data. | [reply] |
|
| [reply] |
|
| [reply] |
|
'Z' comes after 'X'x100, no matter how big you make 100.
| [reply] |
Re: Sort undef
by marinersk (Priest) on Jun 12, 2017 at 12:37 UTC
|
Another way to do it, while certainly not the most efficient, is to replace undefelements with a value guaranteed to both be the maximum value in the comparison and uniquely distinct from the original value set.
I show this inefficient technique more to sow seeds for inspiration for future problems you may run into where setting a maximum value might be a useful idea. Data transmogrification still has value in some cases; having that tool in your toolshed is useful.
Coupled with the approach used by LanX, you could probably craft a fairly efficient and reliable version of this approach.
In this example, I convert the original array such that undefelements are replaced with a unique identifier guaranteed to sort to the bottom of the list (based on the assumption this is a string comparison); the replacement string is guaranteed to not have been found in the original array (again, via an inefficient technique -- I force it to be longer than the longest element in the original array).
From there, the rest of the operation should be relatively intuitive.
#!/usr/bin/perl
use strict;
use warnings;
# Constants (Operationally, if not functionally)
my $MAXCHR = chr 255;
my $HORRUL = '--------------------------------------------------------
+-----------------------';
# Globals
my $Maxval = '';
my @Unsorted = ( 'Dog', 'Cat', 'Bird', undef, 'Elephant', undef, 'Liza
+rd' );
dumpArray("Original:", @Unsorted);
# Build a maximum-value scalar: Length = longest + 1
# We will temporarily replace all undefined elements with this value
# Since it is composed of all chr 255 values, it is the highest for so
+rt
# Since it is one character longer than the longest element, it unique
+ly
# identifies the element as a former undef
# Then sort can do its thing and we can detect these to convert back t
+o undef
my $maxlen = longestElement(@Unsorted);
while (length($Maxval) <= $maxlen) {
$Maxval .= $MAXCHR;
}
# Build a working copy of the array with undef replaced by $Maxval
my @unsortedWork = ();
foreach (@Unsorted) {
if (defined $_) {
push @unsortedWork, $_;
} else {
push @unsortedWork, $Maxval;
}
}
# Sort, Convert modified entries back to undef, and show results
my @sorted_custom = sort @unsortedWork;
my @sorted_final = ();
foreach (@sorted_custom) {
if ($_ eq $Maxval) {
push @sorted_final, undef;
} else {
push @sorted_final, $_;
}
}
dumpArray("Custom Sort:", @sorted_final);
exit;
# Display an array with a title
sub dumpArray {
my ($dumpTitle, @dumpValues) = @_;
print "$HORRUL\n$dumpTitle\n$HORRUL\n";
foreach my $dumpValue (@dumpValues) {
if (defined $dumpValue) {
print "$dumpValue\n";
} else {
print "(undef)\n";
}
}
print "$HORRUL\n";
}
sub longestElement {
my $maxlen = 0;
foreach(@_) {
if (defined $_) {
if (length > $maxlen) { $maxlen = length; }
}
}
return $maxlen;
}
Results:
S:\Steve\Dev\PerlMonks\P-2017-06-12@0734-sort-undef>sort1.pl
----------------------------------------------------------------------
+---------
Original:
----------------------------------------------------------------------
+---------
Dog
Cat
Bird
(undef)
Elephant
(undef)
Lizard
----------------------------------------------------------------------
+---------
----------------------------------------------------------------------
+---------
Custom Sort:
----------------------------------------------------------------------
+---------
Bird
Cat
Dog
Elephant
Lizard
(undef)
(undef)
----------------------------------------------------------------------
+---------
S:\Steve\Dev\PerlMonks\P-2017-06-12@0734-sort-undef>
| [reply] [d/l] [select] |
Re: Sort undef
by Anonymous Monk on Jun 12, 2017 at 14:42 UTC
|
Surprised nobody's suggested the Schwartzian transform on this one. Combined with marinersk's sentinel technique, it's a nice, simple, efficient solution.
@$ResultsFinal = map { $_->[1] }
sort { $a->[0] cmp $b->[0] }
map { [deaccent($_->[$OptOrderToDisplayTable]) // chr(255), $_] }
@$ResultsFinal;
| [reply] [d/l] |
|
That's bloody brilliant.
One question, though. To my eye it looks to be vulnerable to the case where the original list has at least one element which starts with two or more chr(255)characters and at least one element being undef.
Or am I missing something?
| [reply] [d/l] [select] |
|
I'm hoping deaccent will change chr(255) (y with two dots) to a plain y. It also fails if there strings starting with unicode characters above 255. Handling unicode in full generality is a huge pain, so I punted. You probably need something like Unicode::Collate to do it right.
| [reply] |
|
|
|
|
|
|
A reply falls below the community's threshold of quality. You may see it by logging in. |
A reply falls below the community's threshold of quality. You may see it by logging in. |