Home > Net >  How to rearrange dates of the month when they span across end of current month to beginning of next
How to rearrange dates of the month when they span across end of current month to beginning of next

Time:09-17

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 be 10 30 31 1 2 or 30 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
---------------------------------------------
  •  Tags:  
  • perl
  • Related