Code Comments
Programming Forum and web based access to our favorite programming groups.hi, Could someone please help me with the following perl questions. #1 - Is there a way to add "days" to a date? I want end_date=start_date+90days; where start_date is in the format 10/25/04; the output end_date should be in the same format as well. I was not successful loading the Date::Calc module from CSPAN on ActivePerl 5 (Revision 5, version 6, Subrevision 1, - 5.006001). Can some suggest some input. #2 - How could I find the minimum value from table with say 80columns. The numbers are stored in columns across (from column 2 to column 6). The output from the input table should retrieve the minimum value for each unique item in column 0. Does Perl have aggregate function to get minimum/average and other statistical values? How should the array/hash be structured. Please email your feedback to mhassan4@hotmail.com. I will truly appreciate any help that you could offer. Regards, Hassan
Post Follow-up to this messageM. Ilyas Hassan wrote: > #1 - Is there a way to add "days" to a date? I want > end_date=start_date+90days; where start_date is in the format > 10/25/04; the output end_date should be in the same format as well. I > was not successful loading the Date::Calc module from CSPAN on > ActivePerl 5 (Revision 5, version 6, Subrevision 1, - 5.006001). Can > some suggest some input. The standard module Time::Local and the core function localtime() should be sufficient. The solution at http://groups.google.com/groups?sel...40uni-berlin.de may give you some ideas. > #2 - How could I find the minimum value from table with say > 80columns. The numbers are stored in columns across (from column 2 to > column 6). The output from the input table should retrieve the > minimum value for each unique item in column 0. Does Perl have > aggregate function to get minimum/average and other statistical > values? How should the array/hash be structured. I pass that one. But I think you need to provide some sample data, and a serious attempt to solve the problem. > Please email your feedback to mhassan4@hotmail.com. This is a public mailing list, not a private helpdesk. (I sent you a copy, but you should really subscribe to the list if you haven't already.) -- Gunnar Hjalmarsson Email: http://www.gunnar.cc/cgi-bin/contact.pl
Post Follow-up to this message> hi, Hello, > Could someone please help me with the following perl questions. > #1 - Is there a way to add "days" to a date? I want > end_date=start_date+90days; where start_date is in the format 10/25/04; > the output end_date should be in the same format as well. I was not > successful loading the Date::Calc module from CSPAN on ActivePerl 5 > (Revision 5, version 6, Subrevision 1, - 5.006001). Can some suggest > some input. search.cpan.org Date::Calc > #2 - How could I find the minimum value from table with say 80columns. > The numbers are stored in columns across (from column 2 to column 6). > The output from the input table should retrieve the minimum value for > each unique item in column 0. Does Perl have aggregate function to get > minimum/average and other statistical values? How should the array/hash > be structured. search.cpan.org DBI > Please email your feedback to mhassan4@hotmail.com. I will truly We will post back to the list so everyone can share. If want personal consulting and development feel free to contact me offlist for a quote ;p
Post Follow-up to this messageOn Mon, 25 Oct 2004, M. Ilyas Hassan wrote: > #1 - Is there a way to add "days" to a date? The modules that the other two respondants pointed you to are the right way to do this, but it's good to know that the way to do this by hand -- which you won't want to do, but it's good to see why not -- is to only deal with times represented as seconds. So, for example, 90 days works out to 7776000 seconds (60 secs/min * 60 mins/hour * 24 hours/day * 90 days). So you look up the Unix timestamp for the starting date. For this, let's use the time as I type this, 1098746228: $ perl -le 'print scalar localtime 1098746228' Mon Oct 25 19:17:08 2004 $ You add 90 days worth of seconds to that to get the future date: $ perl -le 'print 1098746228 + 7776000' 1106522228 $ And then you convert that back to a more conventional format: $ perl -le 'print scalar localtime( 1098746228 + 7776000 )' Sun Jan 23 18:17:08 2005 $ Or, putting it all together: $ perl -le '$now = 1098746228; $ninety_days = 7776000; $then = $now + $ninet y_days; print scalar localtime $then' Sun Jan 23 18:17:08 2005 $ This isn't *that* bad, but the math gets annoying to manage manually. Using a module like Date::Calc saves a lot of effort & reduces errors. Look up the documentation for Date::Calc for usage detals. > #2 - How could I find the minimum value from table with say 80columns. How is this table represented? Is it in a database? Is it in a flat file of some kind? Do you already have it in memory in some kind of data structure? The approach to this problem depends a lot on what tools you have to work with. For this, you need to describe where this data is coming from, how you are thinking of representing it in variables, and (most important) you need to show us what code you've written to solve the problem. The date problem was a gimme, but this is a larger task with many ways it could be solved. You have to show us where you're starting from before you can expect people to fill in the blanks for you. > Please email your feedback to mhassan4@hotmail.com. I will truly > appreciate any help that you could offer. This seems to be a typo -- all discussion should stay on the list where it can be of use to everyone that is trying to learn this stuff. If you have not already done so, please subscribe to the list so that we can have the conversation on the list. -- Chris Devers
Post Follow-up to this messagehi Chris,
Thanks for the responses.
Q1: Your suggestion would work without the date module for now :-)
Q2: The input file is a flat file (.txt) parsed by "~" with 40 columns.
Columns 2-40 has numbers (all integers, +ve and -ve numbers). Column 0 has
items in alphanumeric and column 1 has labels in text. I would like to find
the minimum value for numbers in columns 2-40 and write out an output in the
form of a flat file with 2 columns (col. 0 = item, col. 1 =min value).
Please see the code I wrote. I does NOT work. Thanks for your input and
help,
=====================
$i=0;
open INV_RPT, "$_inventory_rpt" or die "Can't open file handler";
open (INV_RPT_MIN, ">$_inventory_rpt_min");
{
chop($Ctmp1);
@Ctmp2=split(/~/,$Ctmp1);
$items{$Ctmp2[0]}=$Ctmp2[0];
}
foreach $item (keys %items)
{
while($Ctmp1 = <INV_RPT> )
{
chop($Ctmp1);
@Ctmp2=split(/~/,$Ctmp1);
$START_MIN{$ctmp2[0]}=$ctmp2[2];
for $i (0..40) { if(defined($Ctmp2[$i])) { $Ctmp2[$i] =~ s/\s*//; }
else { $Ctmp2[$i] = ""; } }
if($START_MIN{$ctmp2[0]} < $Ctmp2[$i])
{
$END_MIN{$Ctmp2[0]}=$Ctmp2[$i];
print INV_RPT_MIN "$item~$END_MIN{$Ctmp2[0]}\n";
}
}
}
close(INV_RPT);
close(INV_RPT_MIN);
===============
sample input file content:
==========
col0~col1~col2.......
AI6CQ7~Finishing Onhand~ 48.0~ 42.0~ 134.0~ 124.0~ 120.0~
120.0~ 120.0~ 110.0~ 108.0~ 102.0~ 96.0~ 94.0~ 94.0~
94.0~ 90.0~ 88.0~ 88.0~ 60.0~ 60.0~ 60.0~ 60.0~
60.0~ - 48.0~ -34.0~ 24.0~ 18.0~ 18.0~ 18.0~ 14.0~
98.0~ 88.0~ 88.0~ 88.0~ 88.0~ -88.0~ 82.0~ 78.0~
68.0~ 62.0~ 58.0~ 58.0~ 58.0~ 56.0~ 50.0~ 44.0~
42.0~ 40.0~ 140.0~ 140.0~ 96.0~ 96.0~ 96.0~ 96.0~
96.0~ -96.0~ -96.0~ 56.0~ 56.0~ 56.0~ 56.0~ 56.0~
56.0~ 56.0~ 56.0~ 56.0~ 56.0~ 56.0~
============
I appreciate your help.
>From: Chris Devers <cdevers@pobox.com>
>Reply-To: Perl Beginners List <beginners@perl.org>
>To: "M. Ilyas Hassan" <mhassan4@hotmail.com>
>CC: Perl Beginners List <beginners@perl.org>
>Subject: Re: questions
>Date: Mon, 25 Oct 2004 19:28:38 -0400 (EDT)
>
>On Mon, 25 Oct 2004, M. Ilyas Hassan wrote:
>
>
>The modules that the other two respondants pointed you to are the right
>way to do this, but it's good to know that the way to do this by hand --
>which you won't want to do, but it's good to see why not -- is to only
>deal with times represented as seconds.
>
>So, for example, 90 days works out to 7776000 seconds (60 secs/min * 60
>mins/hour * 24 hours/day * 90 days).
>
>So you look up the Unix timestamp for the starting date. For this, let's
>use the time as I type this, 1098746228:
>
> $ perl -le 'print scalar localtime 1098746228'
> Mon Oct 25 19:17:08 2004
> $
>
>You add 90 days worth of seconds to that to get the future date:
>
> $ perl -le 'print 1098746228 + 7776000'
> 1106522228
> $
>
>And then you convert that back to a more conventional format:
>
> $ perl -le 'print scalar localtime( 1098746228 + 7776000 )'
> Sun Jan 23 18:17:08 2005
> $
>
>Or, putting it all together:
>
> $ perl -le '$now = 1098746228; $ninety_days = 7776000; $then = $now +
>$ninety_days; print scalar localtime $then'
> Sun Jan 23 18:17:08 2005
> $
>
>This isn't *that* bad, but the math gets annoying to manage manually.
>Using a module like Date::Calc saves a lot of effort & reduces errors.
>
>Look up the documentation for Date::Calc for usage detals.
>
>
>How is this table represented? Is it in a database? Is it in a flat file
>of some kind? Do you already have it in memory in some kind of data
>structure?
>
>The approach to this problem depends a lot on what tools you have to
>work with.
>
>For this, you need to describe where this data is coming from, how you
>are thinking of representing it in variables, and (most important) you
>need to show us what code you've written to solve the problem.
>
>The date problem was a gimme, but this is a larger task with many ways
>it could be solved. You have to show us where you're starting from
>before you can expect people to fill in the blanks for you.
>
>
>This seems to be a typo -- all discussion should stay on the list where
>it can be of use to everyone that is trying to learn this stuff. If you
>have not already done so, please subscribe to the list so that we can
>have the conversation on the list.
>
>
>--
>Chris Devers
-----------------------------------------------------------------
Muhammed I. Hassan
1030 Baytowne Drive, #24
Champaign, IL 61822
217-353-0075 (Home)
630-267-2494 (Mobile)
Post Follow-up to this messageM. Ilyas Hassan wrote: > hi, > Could someone please help me with the following perl questions. > > #1 - Is there a way to add "days" to a date? I want > end_date=start_date+90days; where start_date is in the format > 10/25/04; the output end_date should be in the same format as well. I > was not successful loading the Date::Calc module from CSPAN on > ActivePerl 5 (Revision 5, version 6, Subrevision 1, - 5.006001). Can > some suggest some input. The command: ppm install Date-Calc should work for you with ActiveState's Perl. But see also http://datetime.perl.org for the newfangled modules for doing date stuff.
Post Follow-up to this messageM. Ilyas Hassan <mhassan4@hotmail.com> wrote:
: The input file is a flat file (.txt) parsed by "~" with 40
: columns. Columns 2-40 has numbers (all integers, +ve and -ve
: numbers). Column 0 has items in alphanumeric and column 1 has
: labels in text. I would like to find the minimum value for
: numbers in columns 2-40 and write out an output in the form of
: a flat file with 2 columns (col. 0 = item, col. 1 =min value).
That's 41 columns (0 - 40). 40 columns would be numbered
0 - 39.
: Please see the code I wrote. I does NOT work. Thanks for your
: input and help,
Always begin your code with these modules. They will help you
to use better programming habits.
use strict;
use warnings;
: $i=0;
Too early. Declare your variables when you use them.
: open INV_RPT, "$_inventory_rpt" or die "Can't open file handler";
: open (INV_RPT_MIN, ">$_inventory_rpt_min");
$_inventory_rpt and $_inventory_rpt_min have not been defined.
The first 'open' checks for errors and the second does not.
Why? Be consistent. If you do error checking the first time, do
it the last and every time in between.
As long as you use perl you will be opening files. Create a
macro for opening files in your code editor. Try to use the same
open idiom each time. Including the $! variable will help you
track down where errors came from. Here's mine.
my $file = '';
open FH, $file or die qq(Cannot open "$file": $!);
close FH;
Whatever you choose, use the same thing for each 'open'
statement.
: {
: chop($Ctmp1);
: @Ctmp2=split(/~/,$Ctmp1);
: $items{$Ctmp2[0]}=$Ctmp2[0];
: }
Too early. $Ctmp1 (horrible name for a variable) has not been
defined yet. The hash doesn't hold what you expect. Learn how to
test.
use Data::Dumper 'Dumper';
{
chop($temp);
@fields=split(/~/,$temp);
$items{$fields[0]}=$fields[0];
}
print Dumper \%items;
__END__
I get:
$VAR1 = {
'' => undef
};
So %items has one key with an undefined value. I am not sure
what you intended to do here, but that wasn't it.
: foreach $item (keys %items)
: {
The hash has one element. We can skip the loop and just write
this. That doesn't look right, does it?
my $item = '';
: while($Ctmp1 = <INV_RPT> )
: {
Under strict, that would be written like this. (Read perlfunc
'my') I used $record instead of $Ctmp1 because it is more
descriptive of what the variable holds. Use meaningful variable
names.
while( my $record = <INV_RPT> )
{
: chop($Ctmp1);
"chop" is deprecated. Throw away whatever tool (book,
tutorial, outdated program, etc,) you are using to learn perl. Go
to learn.perl.org for books and online support for writing good
perl scripts.
chomp $record;
: @Ctmp2=split(/~/,$Ctmp1);
@Ctmp2 as used here has file scope. Let's scope it to this
code block (using 'my') and let's use a meaningful name for it.
my @fields = split /~/, $line;
: $START_MIN{$ctmp2[0]}=$ctmp2[2];
Why does this hash (%START_MIN) use all caps for the the
variable name? Other variables are mixed case (which I personally
despise). What is so special about this hash that it should
deserve all caps?
According to your description we are attempting to find the
minimum value in this array. We can do that by importing a
function from List::Util. To import this function we need to "use"
this module (at the beginning of the script). The function we need
is named 'min'.
use List::Util 'min';
: for $i (0..40) {
Forty fields, not forty-one.
foreach my $i ( 0 .. 39 ) {
: if ( defined $fields[$i] ) {
Every field in @fields will be defined. "split" cannot return
undefined fields as it is used here. It *can* return empty fields
(''). The only undefined fields are the fields added for columns
with less than 40 columns.
: $fields[$i] =~ s/\s*//;
: } else {
: $fields[$i] = "";
: }
: }
We could use this to eliminate the space between some negative
numbers and the value.
s/\s+//g foreach @fields;
: if($START_MIN{$ctmp2[0]} < $Ctmp2[$i])
: {
: $END_MIN{$Ctmp2[0]}=$Ctmp2[$i];
: print INV_RPT_MIN "$item~$END_MIN{$Ctmp2[0]}\n"; }
: }
You did say this code didn't work. Finding a minimum value
in an array would require iterating over each element, not just
these.
We don't need any of that to get a minimum value with the
imported min() function.
my $item = shift @fields;
s/\s+//g foreach @fields;
my $minimum = min( @fields );
print INV_RPT_MIN "$item~$minimum\n";
: }
: close(INV_RPT);
: close(INV_RPT_MIN);
: ===============
As tested:
use strict;
use warnings;
use List::Util 'min';
while ( <DATA> ) {
my @fields = split /~\s*/;
my $item = shift @fields;
s/\s+//g foreach @fields;
my $minimum = min( @fields );
print "$item~$minimum\n";
}
__END__
AI6CQ7~Finishing Onhand~ 48.0~ 42.0~ 134.0~ 124.0~
120.0~ 120.0~ 120.0~ 110.0~ 108.0~ 102.0~ 96.0~
94.0~ 94.0~ 94.0~ 90.0~ 88.0~ 88.0~ 60.0~
60.0~ 60.0~ 60.0~ 60.0~ - 48.0~ -34.0~ 24.0~
18.0~ 18.0~ 18.0~ 14.0~ 98.0~ 88.0~ 88.0~
88.0~ 88.0~ -88.0~ 82.0~ 78.0~ 68.0~ 62.0~
58.0~ 58.0~ 58.0~ 56.0~ 50.0~ 44.0~ 42.0~
40.0~ 140.0~ 140.0~ 96.0~ 96.0~ 96.0~ 96.0~
96.0~ -96.0~ -96.0~ 56.0~ 56.0~ 56.0~ 56.0~
56.0~ 56.0~ 56.0~ 56.0~ 56.0~ 56.0~ 56.0~
HTH,
Post Follow-up to this message
Show a Printable Version
Email This Page to Someone!
Receive updates to this thread
Powered by vBulletin
Copyright 2000-2006 Jelsoft Enterprises Limited.