marlowvoltron has asked for the wisdom of the Perl Monks concerning the following question:

Ok monks, so I have perl going through directories, and creating hashes out of .tifs it finds. The keys are the dates and the values are the .tifs (to keep them associated). So these hashes are always differing in size.

How do I create a loop where I have the first key subtract from the next key, so I can calculate the difference in days? i will want it to do this for every subsequent key. So if I have 4 keys, I will want element 0 to subtract from 1, 2, & 3. Then have element 1 subtract from 2 & 3. And then element 2 to subtract from 3.

Replies are listed 'Best First'.
Re: Hash Math
by kennethk (Abbot) on Jun 09, 2014 at 19:48 UTC
    What have you tried? What worked? What didn't? See How do I post a question effectively?.

    First, I would suggest using ISO_8601 as your date format, since this sorts easily. For the date arithmetic, I would suggest using a module to do the math, since dealing with days-of-month variation is an unnecessary headache. DateTime is a common choice for that. You could implement the looping component using two nested loops, a la:

    for my $time1 (sort keys %hash) { for my $time2 (sort keys %hash) { next if $time2 < $time1; ... # And do the math } }

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      When I try your code, I get this Can't modify subtraction (-) in scalar assignment

        There is no subtraction in my posted code. What code did you run? My best guess is that you've forgotten some quoting around your dates.

        Note that if you are using ISO 8601 w/ hyphens, the numerical comparison (the next if... bit) will break down, and so you'll need to use DateTime's built-in comparator.


        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Hash Math
by boftx (Deacon) on Jun 09, 2014 at 23:08 UTC

    Your task is recursive in nature. Here is one way to approach it:

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %tifs = ( '2014-06-01 00:00:00' => 'file_01.tif', '2014-06-02 00:00:00' => 'file_02.tif', '2014-06-03 00:00:00' => 'file_03.tif', '2014-06-04 00:00:00' => 'file_04.tif', ); hashmath( sort( keys( %tifs ) ) ); exit; sub hashmath { my @times = @_; print '@times has ' . scalar( @times ) . " elements\n"; print Dumper(\@times); my $time = shift( @times ); for my $next_time ( @times ) { # do math and whatever here print "Subtracting $time from $next_time\n"; } hashmath( @times ) if @times > 1; return; } __END__
    Here is the output:
    $ perl hashmath.pl @times has 4 elements $VAR1 = [ '2014-06-01 00:00:00', '2014-06-02 00:00:00', '2014-06-03 00:00:00', '2014-06-04 00:00:00' ]; Subtracting 2014-06-01 00:00:00 from 2014-06-02 00:00:00 Subtracting 2014-06-01 00:00:00 from 2014-06-03 00:00:00 Subtracting 2014-06-01 00:00:00 from 2014-06-04 00:00:00 @times has 3 elements $VAR1 = [ '2014-06-02 00:00:00', '2014-06-03 00:00:00', '2014-06-04 00:00:00' ]; Subtracting 2014-06-02 00:00:00 from 2014-06-03 00:00:00 Subtracting 2014-06-02 00:00:00 from 2014-06-04 00:00:00 @times has 2 elements $VAR1 = [ '2014-06-03 00:00:00', '2014-06-04 00:00:00' ]; Subtracting 2014-06-03 00:00:00 from 2014-06-04 00:00:00 $

    You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: Hash Math
by locked_user sundialsvc4 (Abbot) on Jun 09, 2014 at 20:01 UTC

    This will not be as easy as it first appears.   First of all, there is no concept of “previous” or “next” with regards to a hashref.   Also, dates can be difficult to manage as hash-key strings.   So, do you really need for this to be a hash, or will a simple array/list do just as well?

    Guessing that the answer to that question is most-likely, “yes,” I suggest that each list-element should be a hashref which contains both the data-record and the associated date ... storing the latter as a date/time object against which math can easily be done.   Now, it is possible to sort that list, using a sort-comparison function that accesses the dates in each record (as the date/time objects that they actually are).   It is then possible to iterate through each element in these lists to do whatever comparative processing might be needed.

    For lists of reasonable size (and “reasonable size” can easily be human-large ...), functions like grep are handy for currying through the list and retrieving matching entries.

      I have an array for just the dates, I actually zipped together my @dates array and my @tifs array to create the %hash. So if it would be easier doing the math in the array, that's fine, since I have the elements in the hash, I can always reference the hash later to get the tif corresponding to the date.

      So, would I just use each to iterate through my date array to do the necessary comparisons?

        Here is a way to get the difference between the dates in days from an array of dates. (Note that the dates are sorted. If your array isn't sorted you would have some negative differences).
        #!/usr/bin/perl use strict; use warnings; use Time::Piece; my @date = ( '2014-06-01', '2014-06-02', '2014-06-03', '2014-06-04' ); for my $i (0 .. $#date-1) { my $d1 = Time::Piece->strptime($date[$i], '%Y-%m-%d'); for my $j ($i+1 .. $#date) { my $d2 = Time::Piece->strptime($date[$j], '%Y-%m-%d'); my $diff = $d2 - $d1; print $d1->ymd, " and ", $d2->ymd, " is ", $diff->days, "\n"; } }
        Prints
        2014-06-01 and 2014-06-02 is 1 2014-06-01 and 2014-06-03 is 2 2014-06-01 and 2014-06-04 is 3 2014-06-02 and 2014-06-03 is 1 2014-06-02 and 2014-06-04 is 2 2014-06-03 and 2014-06-04 is 1
        Tie::Hash::Indexed maintains an ordered hash. Also, use each only if you're aware of the fact that breaking an iteration partly through the hash means that subsequent iterations will start where it left off. You may be safer with the foreach/keys idiom.

        It may be worth mentioning PDL if you're going to be doing math stuffs with complex data structures. But it might be a bit overkill for your application.