I have to work on data which goes according to dates of the month. When I extract the dates from the filename I get a random arrangement of the dates, as below.
31 02 28 30 27 01 29 03 04
This is processing the data from 27th August to 4th September. I need the dates arranged sequentially from 27th to 4th. I have to process 9 dates at a time. This problem arises at the end of the month when I have this month's dates (large figures) and next month (small figures).So I sort the array which I call @fdaylist. I get
01 02 03 04 27 28 29 30 31.
To get what I want the script snippet below, does the job and gives
27 28 29 30 31 01 02 03 04
I am sure if this script can work all the time. It reproduces the sequence several time over, so I have to use the uniq() function. I will appreciate if I can get a suggestion of a better way of achieving my objective.
my $last_occ=lastidx { /0\d/ } @fdaylist;
if($last_occ > 1){
foreach(@fdaylist){
for(my $mm=$last_occ 1; $mm < @fdaylist; $mm){
push (@fday, $fdaylist[$mm]);
}
for(my $nn=0; $nn <= $last_occ; $nn){
push (@fday, $fdaylist[$nn]);
}
}
}
@fday = uniq(@fday);
CodePudding user response:
I assume that the numbers must be consecutive, with one gap, otherwise it wouldn't be possible to decide which way some belong. I also assume that there always is a gap.† (Nope. While dates are indeed consecutive, they may straddle two months or lie within one, as clarified.)
One way: Go through the sorted array moving items from front to back until reaching a gap
use warnings;
use strict;
use feature 'say';
# Dates are consecutive, either straddling two months or within a month
my @dates = (31, 02, 28, 30, 27, 01, 29, 03, 04); # or can be 05..13 etc
@dates = sort { $a <=> $b } @dates;
if (consec_nums(\@dates)) {
say "Consecutive numbers (dates in same month), nothing to do (@dates)";
}
else {
my $last_moved;
while (1) {
push @dates, $last_moved = shift @dates;
last if $dates[0] > $last_moved 1;
}
@dates = map { sprintf "d", $_ } @dates; # for 01 02 etc
}
say "@dates";
# Returns true (1) if numbers are consecutive, like consecutive dates
# in a single month, false (undef) otherwise. Assumes a sorted array
sub consec_nums {
my ($ra) = @_;
my $prev = $ra->[0];
for my $i (1..$#$ra) {
return if $ra->[$i] > $prev 1;
$prev = $ra->[$i];
}
return 1;
}
Another way: Go over the sorted array to find the last index before the gap (after which numbers stop being consecutive); then splice-and-push
# same sorted @dates, same assumptions. same consec_nums() sub
if (consec_nums(\@dates)) {
say "Consecutive numbers (dates in same month), nothing to do (@dates)";
}
else {
my $consec_idx;
for my $i (0 .. $#dates) {
$consec_idx = $i, last
if $dates[$i] 1 < $dates[$i 1];
}
push @dates, splice @dates, 0, $consec_idx 1;
}
say "@dates";
† For the mentioned assumptions:
If we could have
1 2 10 30 31
then should that be10 30 31 1 2
or30 31 1 2 10
?If there were no gap (all dates in one month), like
05 .. 13
, then the procedures above would break. If such a case is possible then first test for it.It has been clarified in a comment that this is indeed possible, that all dates are inside one month. The answer above has then been amended with the test for such a case.
CodePudding user response:
Please see if the following sample code performs operation you described in your post.
The code:
- reads dates into a string
- splits the sting and sorts dates into an array
- find index of last day starting with
0\d
- re-arranges the array with two array slices
Note: print out spits input, ordered and re-ordered data
use strict;
use warnings;
use feature 'say';
while( <DATA> ) {
chomp;
say 'GOT: ' . $_;
my @dates = sort split ' ', $_;
say 'IN: ' . join(' ',@dates);
my $index = -1;
/0\d/ && $index for @dates;
@dates = (@dates[$index 1..$#dates],@dates[0..$index]);
say 'OUT: ' . join(' ', @dates);
say '-' x 45;
}
__DATA__
31 02 28 30 27 01 29 03 04
28 03 26 02 25 01 23 22 24
Output
GOT: 31 02 28 30 27 01 29 03 04
IN: 01 02 03 04 27 28 29 30 31
OUT: 27 28 29 30 31 01 02 03 04
---------------------------------------------
GOT: 28 03 26 02 25 01 23 22 24
IN: 01 02 03 22 23 24 25 26 28
OUT: 22 23 24 25 26 28 01 02 03
---------------------------------------------