1

Related/possible duplicate: Why do I get "uninitialized value" warnings when I use Date::Manip's sortByLength?

This block of code:

my @sorted_models = sort { 
    UnixDate($a->{'year'}, "%o") <=>
    UnixDate($b->{'year'}, "%o") 
} values %{$args{car_models}};

kept generating the following error warning:

Use of uninitialized value in length at /.../Date/Manip.pm line 244.

Date::Manip is a CPAN module. And line 244 of Date::Manip is found within the following block of code:

# Get rid of a problem with old versions of perl
no strict "vars";
# This sorts from longest to shortest element
sub sortByLength {
    return (length $b <=> length $a);
}
use strict "vars";

But then including this (printing out the actual Unix Date value to the console in the logger) before the block of code to sort the values:

foreach (values %{$args{car_models}}) {
    $g_logger->info(UnixDate($_->{'year'},"%o")); 
}

removed the errors warnings entirely. Why? And what is a good fix instead of doing all these logging statements?

NOTE: None of the sorted values are undefined because when I printed them out in the logger, I could see that every one of them had a numerical value.

Community
  • 1
  • 1
Kys
  • 2,597
  • 5
  • 20
  • 16
  • Can you put your logging statement in the sort block to see the actual values of $a and $b? – zakovyrya Aug 12 '09 at 18:28
  • Also I would rather not touch any code in the CPAN module – Kys Aug 12 '09 at 18:29
  • I meant your sort block, not Date::Manip's. Something like use Data::Dumper; my @sorted_models = sort { print Dumper {a => $a, b => $b}; UnixDate($a->{'year'}, "%o") <=> UnixDate($b->{'year'}, "%o") } values %{$args{car_models}}; – zakovyrya Aug 12 '09 at 18:32
  • Does the error show up with any data set? Is there a minimum reproducible test case? – Chris Simmons Aug 12 '09 at 18:32
  • So I added that line of code to dump my data: my @sorted_models = sort { $g_logger->info(Dumper{a=>$a,b=>$b}); UnixDate($a->{'year'}, "%o") <=> UnixDate($b->{'year'}, "%o"); } values %{$args{car_models}}; I was able to dump $a and $b ONCE, then I got the error like >50 times, followed by a dump of $a and $b for 20 or so times (I have 10 elements in my array) – Kys Aug 12 '09 at 18:39
  • 2
    What the heck are you doing? http://stackoverflow.com/questions/1267706/perl-hash-ref-error – Sinan Ünür Aug 12 '09 at 18:56
  • Here is how my data structure looks like: { 'four-wheel' => 'true', 'description' => 'Lightning fast', 'producer' => { 'name' => {} }, 'year' => '2009-08-07T22:31:06Z', }; – Kys Aug 12 '09 at 19:08

3 Answers3

1

I am going to one last time try to answer this as clearly as possible.

First, if all timestamps are like 2008-08-07T22:31:06Z, there is no need to map them through UnixDate as standard sort using cmp will sort them correctly.

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
use Date::Manip;

my %args = (
    car_models => {
        a => { year => '2009-08-07T22:31:06Z' },
        b => { year => '2008-08-07T23:31:06Z' },
        c => { year => '2008-08-07T21:31:06Z' },
    },
);

my @sorted_cmp = sort {
    $a->{year} cmp $b->{year}
} values %{ $args{car_models}};

print "Sorted *without* using UnixDate:\n";
print Dumper \@sorted_cmp;

my @sorted_dm = sort {
    UnixDate($a->{year}, '%o') <=> UnixDate($b->{year}, '%o')
} values %{ $args{car_models}};

print "Sorted using UnixDate:\n";
print Dumper \@sorted_dm;

Output (after setting TZ in cmd to placate Date::Manip):

C:\Temp> cars
Sorted *without* using UnixDate:
$VAR1 = [
          {
            'year' => '2008-08-07T21:31:06Z'
          },
          {
            'year' => '2008-08-07T23:31:06Z'
          },
          {
            'year' => '2009-08-07T22:31:06Z'
          }
        ];
Sorted using UnixDate:
$VAR1 = [
          {
            'year' => '2008-08-07T21:31:06Z'
          },
          {
            'year' => '2008-08-07T23:31:06Z'
          },
          {
            'year' => '2009-08-07T22:31:06Z'
          }
        ];

No warnings, no errors ... Ergo, all you have put on this page is one big mess of a red herring. Besides, this still does not explain where the 1249998666 in your other question came from.

Community
  • 1
  • 1
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • H-ha, that's easy one. He put unix timestamps (product of UnixDate) into an array of supposedly hash references of automotive data and later he tried to operate them as if they are hash references: push(@not_sorted_models, UnixDate($_->{'year'},"%o")); hence the error Can't use string ("1249998666") as a HASH ref – zakovyrya Aug 12 '09 at 21:36
  • 1
    @zakovyrya precisely why I have been asking the OP to show the actual data structures and code he is using and trying to discourage others from going on a wild goose chase with the fragments he has posted in three separate questions so far. – Sinan Ünür Aug 12 '09 at 21:39
  • @Sinan Ünür: Sometimes it adds to fun :) Isn't that what this site is all about? – zakovyrya Aug 12 '09 at 21:41
  • Thanks for everything. I feel like I just pissed off a few people. The string would be the UnixDate value of a year field in the automotive data. I was thinking UnixDate($_->{'year'},"%o") would return a straight-up integer and not a hash ref, so I thought I was operating with a number. – Kys Aug 12 '09 at 21:44
  • Yes YYMMDD form of the date will allow you to sort correctly. I thought an integer comparison would be safest. – Kys Aug 12 '09 at 21:46
  • http://www.codinghorror.com/blog/archives/001101.html *Stackoverflow is sort of like the anti-experts-exchange ... with the ultimate intent of collectively increasing the sum total of **good programming knowledge** in the world. ... Better programming is our goal.* Emphasis mine. – Sinan Ünür Aug 12 '09 at 21:47
  • @Kys: No, that's exactly what UnixDate returns - straight-up integer, probably what you intended to do is to convert your datastamps into integers, in this case you had to do $_->{year} = UnixDate($_->{year}, '%o'); push @not_sorted_models, $_; – zakovyrya Aug 12 '09 at 21:48
  • @Kys regardless of what you thought one fact remains: It is not possible to reproduce your problem with the information you posted. – Sinan Ünür Aug 12 '09 at 21:49
  • @zakovyrya: Yes, that makes more sense that I would want to insert the whole structure into the array if at the end of my sorting process I wanted to determine the make of the car with the "top" year. Now what if I just wanted to insert the year values (after converting into UnixDate form) @Sinan Ünür: Hopefully the emphasis will be mine one fine day. :) – Kys Aug 12 '09 at 21:53
-2

Something is wrong with Date::Manip in the way it localizes special variables. Try to call Date_Init() before you do sorting. It seems to solve the problem:

use strict;
use warnings;

use Data::Dumper;
use Date::Manip qw(UnixDate Date_Init);
my $cars_ref = {
    mazda  => {model => 'mazda', year => '2008' },
    toyota => {model => 'toyota', year => '2001' },
    mitsu  => {model => 'mitsu', year => '2005' }
};

Date_Init(); # Initialize Date::Manip first!

my @models =
  sort {
    UnixDate( $a->{year}, '%o' ) <=> UnixDate( $b->{year}, '%o' );
  } values %$cars_ref;
print Dumper \@models;

Output:

$VAR1 = [
          {
            'model' => 'toyota',
            'year' => '2001'
          },
          {
            'model' => 'mitsu',
            'year' => '2005'
          },
          {
            'model' => 'mazda',
            'year' => '2008'
          }
        ];
zakovyrya
  • 9,579
  • 6
  • 39
  • 28
  • 3
    A real question is why anyone would want to map the years above through `UnixDate` to sort. – Sinan Ünür Aug 12 '09 at 19:31
  • 1
    Besides, omitting the `Date_Init()` call above does not give rise to uninitialized value warnings. On Windows, it pukes because it can't figure out the timezone. Setting `TZ` in the shell fixes that. On Linux, the code above runs fine without the `Date_Init()`. So, your answer does not seem to be related to the OP's problem. – Sinan Ünür Aug 12 '09 at 19:36
  • Why wouldn't anyone want to map the years above through UnixDate to sort? – Kys Aug 12 '09 at 20:00
  • Actuall zakovyra's solution took away my warnings. – Kys Aug 12 '09 at 20:00
  • @Kys There is no need to map years such as `'2001'`, `'2005'` and `'2008'` through `UnixDate` because they will sort just fine using regular `<=>` (or even `cmp`). No one can know what is going on with your code and data because you have not posted a short self contained script that works on a small section of your data. All we have are bits and pieces here and there and this is a consistent theme across your three posts on seemingly the same issue. – Sinan Ünür Aug 12 '09 at 20:15
  • @Sinan Ünür I put it merely as an illustration due to lack of actual data, which wasn't available by that time – zakovyrya Aug 12 '09 at 20:22
  • @zakovyrya I understand why you did what you did. – Sinan Ünür Aug 12 '09 at 20:40
  • @zakovyrya However, note http://search.cpan.org/perldoc/Date::Manip *The above routines all check to make sure that Date_Init is called. If it hasn't been, they will call it automatically. As a result, there is usually no need to call Date_Init explicitly unless you want to change some of the config variables* – Sinan Ünür Aug 12 '09 at 20:49
  • @Sinan Ünür: Oh right I was still thinking you were referring to the years as timestamps as I had given in my previous example. But given the data structure I posted, is that sufficient information to work off on? If so, why is it that Date_Init() was able to remove all the warnings? Or is more information required for that? – Kys Aug 12 '09 at 21:00
  • @Kys @Sinan Ünür: As I said before it's probably because of the way they localize/do not localize special variables - $a and $b in this case. I noticed that they play well with $_: line local($_)=(); in sub Date_Init(). Although it's annoying, it's not that hard to fix it – zakovyrya Aug 12 '09 at 21:06
  • @zakovyrya The `$a` and the `$b` in `package Date::Manip` have nothing to do with the `$a` and the `$b` in `package main`. – Sinan Ünür Aug 12 '09 at 21:32
  • @Sinan Ünür: Maybe not, don't feel like digging into this right now – zakovyrya Aug 12 '09 at 21:44
  • @zakovyrya -1. `$a` and `$b` in the sort block are package global. – Sinan Ünür Aug 12 '09 at 22:09
-3

So I added that line of code to dump my data:

 my @sorted_models =    sort { $g_logger->info(Dumper{a=>$a,b=>$b});
                        UnixDate($a->{'year'}, "%o") <=>
                        UnixDate($b->{'year'}, "%o"); }
                        values %{$args{car_models}};

I was able to dump $a and $b ONCE, then I got the error like >50 times, followed by a dump of $a and $b for 20 or so times (I have 10 elements in my array)

Kys
  • 2,597
  • 5
  • 20
  • 16