简体   繁体   中英

sort 2 key hash by value

i keep learning hashes and various things u can do with them. taday i have this question. how do i sort a hash by value, when i have 2 keys in it? and how do i print it out? i have a csv file. im trying to store values in the hash, sort it by value. this way I'll be able to print the biggest and the smallest value, i also need the date this value was there. so far i can print the hash, but i cant sort it.

#!/usr/bin/perl 
#find openMin and openMax. 
use warnings; 
use strict;

my %pick;
my $key1;
my $key2;
my $value;  
my  $file= 'msft2.csv';              
my $lines = 0;
my $date;
my $mm;
my $mOld = "";

my $open;
my $openMin;
my $openMax;
open (my $fh,'<', $file) or die "Couldnt open the $file:$!\n";
while  (my $line=<$fh>)
{ 
    my @columns = split(',',$line);
    $date = $columns[0];
    $open = $columns[1];

    $mm = substr ($date,5,2);

    if ($lines>=1) {                        #first line of file are names of columns     wich i 
    $key1 = $date;                         #dont need. data itself begins with second line 
    $key2 = "open";
    $value = $open;
    $pick{$key1}{"open"}=$value; 


   }
 $lines++;
} 
foreach $key1 (sort keys %pick) {
foreach $key2 (keys %{$pick{$key1}}) {
    $value = $pick{$key1}{$key2};
    print "$key1 $key2 $value \n";

 }
}
exit;

1. Use a real CSV parser

Parsing a CSV with split /,/ works fine...unless one of your fields contains a comma. If you are absolutely, positively, 100% sure that your code will never, ever have to parse a CSV with a comma in one of the fields, feel free to ignore this. If not, I'd recommend using Text::CSV . Example usage:

use Text::CSV;

my $csv = Text::CSV->new( { binary => 1 } )
    or die "Cannot use CSV: " . Text::CSV->error_diag ();

open my $fh, "<", $file or die "Failed to open $file: $!";
while (my $line = $csv->getline($fh)) {
    print @$line, "\n";
}
$csv->eof or $csv->error_diag();
close $fh;

2. Sorting

I only see one secondary key in your hash: open . If you're trying to sort based on the value of open , do something like this:

my %hash = (
    foo => { open => "date1" },
    bar => { open => "date2" },
);

foreach my $key ( sort { $hash{$a}{open} cmp $hash{$b}{open} } keys %hash ) {
    print "$key $hash{$key}{open}\n";
}

(this assumes that the values you're sorting are not numeric. If the values are numeric (eg 3 , -17.57 ) use the spaceship operator <=> instead of the string comparison operator cmp . See perldoc -f sort for details and examples.)

EDIT: You haven't explained what format your dates are in. If they are in YYYY-MM-DD format, sorting as above will work, but if they're in MM-DD-YYYY format, for example, 01-01-2014 would come before 12-01-2013 . The easiest way to take care of this is to reorder the components of your date from most to least significant (ie year followed by month followed by day). You can do this using Time::Piece like this:

use Time::Piece;

my $date = "09-26-2013";
my $t = Time::Piece->strptime($date, "%m-%d-%Y");
print $t->strftime("%Y-%m-%d");

Another tidbit: in general you should only declare variables right before you use them. You gain nothing by declaring everything at the top of your program except decreased readability.

You could concatenate key1 and key2 into a single key as:

$key = "$key1 key2";
$pick{$key} = $value;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM