Re: Date to be sorted in descending and time in ascending
by RichardK (Parson) on May 18, 2012 at 09:38 UTC
|
my $dt = '20090405022100';
my $d = substr $dt,0,8;
my $t = substr $dt,8;
Then read the perlfaq on sort How do I sort an array by (anything)? | [reply] [Watch: Dir/Any] [d/l] |
Re: Date to be sorted in descending and time in ascending
by roboticus (Chancellor) on May 18, 2012 at 09:48 UTC
|
karthik7887:
You just need a custom sort method. Break the string into the date part and time part, and give the proper comparisons. Something like this:
my @sorted = sort { substr($b,0,8) <=> substr($a,0,8) || substr($a,8)
+<=> substr($b,8) } @unsorted;
The first chunk breaks the date part off of the string, and sorts it in reverse order (note $b is on the left, $a on the right). In the case when the dates are the same, it breaks the time off the string and sorts it in ascending order ($a on the left, $b on the right).
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [Watch: Dir/Any] [d/l] |
|
This means that the substr function will be called four times for each comparison, which could add up if it's a large array. That makes it a good candidate for a Schwartzian Transform. (You probably know that; I'm adding it for the original poster.) UPDATE: I was completely wrong about this; see below.
#!/usr/bin/env perl
use Modern::Perl;
use Data::Dumper;
my @data = <DATA>;
@data =
map { $_->[0] }
sort { $b->[1][0] <=> $a->[1][0] or $a->[1][1] <=> $b->[1][1] }
map { [ $_, [substr( $_, 0, 8 ), substr( $_, 8)]] }
@data;
say @data;
__DATA__
20090405022300
20080405022600
20090405022900
20080405023500
20050405005000
20080405022500
20090405022500
20020405081200
20010405000000
20090405022100
UPDATE: While I understand the Schwartzian Transform in theory and think it's one of the coolest things ever, I haven't had much call to actually use it, so I did a benchmark for this case against the simple sort of substr calls. I was a little surprised to see that the repeated substr calls beat my ST, testing with array sizes from 10 to 1_000_000. In fact, the ST took about twice as long in all tests. I guess four substr calls (or two if the first comparison returns a value so the second comparison isn't necessary) don't qualify as expensive enough to make the overhead of the ST worth it here. Darn it.
Aaron B.
Available for small or large Perl jobs; see my home node.
| [reply] [Watch: Dir/Any] [d/l] |
|
aaron_baugher:
Hmmm, I'm surprised. I figured at a million strings that the Schwartzian Transform would win. But you show another good lesson: Measure, don't guess. While both you and I expected the transform to win at a million strings, measurement trumps expectation.
...roboticus
When your only tool is a hammer, all problems look like your thumb.
| [reply] [Watch: Dir/Any] |
|
Re: Date to be sorted in descending and time in ascending
by kcott (Archbishop) on May 18, 2012 at 09:48 UTC
|
my $primary_sort_column = n;
my $secondary_sort_column = n;
my $tertiary_sort_column = n;
my @sorted_records = sort {
$a->[$primary_sort_column] op $b->[$primary_sort_column];
||
$a->[$secondary_sort_column] op $b->[$secondary_sort_column];
||
$a->[$tertiary_sort_column] op $b->[$tertiary_sort_column];
) @unsorted_records;
n is an integer; op is typically cmp (lexical ordering) or the spaceship operator <=> (numeric ordering)
Take a look at sort for details.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Date to be sorted in descending and time in ascending
by tobyink (Canon) on May 18, 2012 at 17:02 UTC
|
use 5.010;
use strict;
use Sort::Key qw(multikeysorter);
my $sorter = multikeysorter(
sub { /^(.{8})(.{6})/ }, # how to split into keys
qw( -int int ), # how to sort each key
);
my @dateArray = grep { 1+chomp } <DATA>;
say for $sorter->(@dateArray);
__DATA__
20010405000000
20050405005000
20020405081200
20080405022500
20080405022600
20080405023500
20090405022500
20090405022300
20090405022900
20090405022100
perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
| [reply] [Watch: Dir/Any] [d/l] |
|
use Sort::Key::Maker sort_dates => sub { /(.{8})(.{6})/ }, qw(-int int
+);
my @sorted = sort_dates(@dateArray);
| [reply] [Watch: Dir/Any] [d/l] |
Re: Date to be sorted in descending and time in ascending
by salva (Canon) on May 18, 2012 at 17:34 UTC
|
substr($_, 0, 8) =~ tr/0-9/9876543210/ for @dateArray;
@dateArray = sort @dateArray;
substr($_, 0, 8) =~ tr/0-9/9876543210/ for @dateArray;
| [reply] [Watch: Dir/Any] [d/l] |
Re: Date to be sorted in descending and time in ascending
by johngg (Canon) on May 18, 2012 at 21:04 UTC
|
knoppix@Microknoppix:~$ perl -E '
> my @dates = qw{
> 20010405000000
> 20050405005000
> 20020405081200
> 20080405022500
> 20080405022600
> 20080405023500
> 20090405022500
> 20090405022300
> 20090405022900
> 20090405022100
> };
>
> say for
> map { substr $_, 8 }
> sort
> map { pack q{NNA*}, 0 - substr( $_, 0, 8 ), 0 + substr( $_, 8 ),
+ $_ }
> @dates;'
20090405022100
20090405022300
20090405022500
20090405022900
20080405022500
20080405022600
20080405023500
20050405005000
20020405081200
20010405000000
knoppix@Microknoppix:~$
| [reply] [Watch: Dir/Any] [d/l] |
Re: Date to be sorted in descending and time in ascending
by Cristoforo (Curate) on May 18, 2012 at 23:27 UTC
|
Using unpack in a Schwartzian Transform.
#!/usr/bin/perl
use strict;
use warnings;
use 5.014;
print map {$_->[0]}
sort {$b->[1] cmp $a->[1] || $a->[2] cmp $b->[2]}
map {[ $_, unpack "a8a6", $_ ]} <DATA>;
__DATA__
20010405000000
20050405005000
20020405081200
20080405022500
20080405022600
20080405023500
20090405022500
20090405022300
20090405022900
20090405022100
| [reply] [Watch: Dir/Any] [d/l] |
|
I wondered if someone would suggest unpack. I don't use it often enough to remember the format codes without checking the man page, but it seems like it's often the fastest solution for this kind of thing. So I added yours to my benchmark, and found that unpack was slightly slower than substr/substr in this case. (I'd guess that if it were necessary to break the string into three or more pieces, unpack would come out ahead.) Both were still slower than the non-Schwartzian substr/substr sort, as detailed in my other post, though. Results and code:
bannor:~/work/perl/monks$ perl 971240.pl 1000000
s/iter stunpack stsubst plainsort
stunpack 12.4 -- -10% -31%
stsubst 11.2 11% -- -24%
plainsort 8.54 46% 31% --
bannor:~/work/perl/monks$ cat 971240.pl
#!/usr/bin/env perl
use Modern::Perl;
use Benchmark qw(:all);
my @data;
push @data, int(rand(1000000000000))+10000000000000 for (1..$ARGV[0]);
cmpthese( 10, {
'stunpack' => \&stunpack,
'stsubst' => \&stsubst,
'plainsort' => \&plainsort,
});
sub plainsort {
my @d = sort {
substr($b,0,8) <=> substr($a,0,8)
or substr($a,8) <=> substr($b,8)
} @data;
}
sub stsubst {
my @d =
map { $_->[0] }
sort { $b->[1] <=> $a->[1] or $a->[2] <=> $b->[2] }
map { [ $_, substr( $_, 0, 8 ), substr( $_, 8)] }
@data;
}
sub stunpack {
my @d =
map { $_->[0] }
sort { $b->[1] <=> $a->[1] or $a->[2] <=> $b->[2] }
map { [ $_, unpack "a8a6" ] }
@data;
}
Aaron B.
Available for small or large Perl jobs; see my home node.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] |