开发者

array to hash in perl

I have a source list fro开发者_如何学编程m which I am picking up random items and populating the destination list. The item that are in the list have a particular format. For example:

item1{'name'}
item1{'date'}

etc and many more fields.

while inserting into the destination list I check for unique names on items and insert it into that list. For this I have to traverse the entire destination list to check if an item with a given name exists and if not insert it.

I thought it would be nice if I make the destination list as hash instead of a list again so that I can look up for the item faster and efficiently. I am new to Perl and am not getting how to do this. Anybody, Please help me on how to insert an item, find for a particular item name, and delete an item in hash?

How can I make both the name and date as key and the entire item as value?


my %hash;

  1. Insert an item $V with a key $K?

    $hash{$K} = $V

  2. Find for a particular name / key $K?

    if (exists $hash{$K}) { 
        print "it is in there with value '$hash{$K}'\n";
    } else { 
        print "it is NOT in there\n" 
    }
  1. Delete a particular name / key?

    delete $hash{$K}

  2. Make name and date as key and entire item as value?

Easy Way: Just string everything together

set: $hash{ "$name:$date" } = "$name:$date:$field1:$field2"
get: my ($name2,$date2,$field1,$field2) = split ':', $hash{ "$name:$date" }
del: delete $hash{ "$name:$date" }

Harder Way: Store as a hash in the hash (google "perl object")

set:

my %temp;
$temp{"name"} = $name;
$temp{"date"} = $date;
$temp{"field1"} = $field1;
$temp{"field2"} = $field2

$hash{"$name:$date"} = \$temp;

get:

my $find = exists $hash{"$name:$date"} ? $hash{"$name:$date"} : undef;
if (defined find) { # i.e. it was found
    printf "field 1 is %s\n", $find->{"field1"}
} else {
    print "Not found\n";
}

delete:

delete $hash{"$name:$date"}


It is not easy to understand what you are asking because you do not describe the input and the desired outputs specifically.

My best guess is something along the lines of:

#!/usr/bin/perl

use strict; use warnings;

my @list = (
    q(item1{'name'}),
    q(item1{'date'}),
);

my %lookup;

for my $entry ( @list ) {
    my ($name, $attrib) = $entry =~ /([^{]+){'([^']+)'}/;
    $lookup{ $name }{ $attrib } = $entry;
}

for my $entry ( keys %lookup ) {
    my %entry = %{ $lookup{$entry} };
    print "@entry{keys %entry}\n"
}

use YAML;
print Dump \%lookup;

Output:

item1{'date'} item1{'name'}
---
item1:
  date: "item1{'date'}"
  name: "item1{'name'}"


If you know what items, you are going to need and what order you'll need them in for keys, then re parsing the key is of questionable value. I prefer to store them in levels.

$hash{ $h->{name} }{ $h->{date} } = $h;
# ... OR ...
$hash{ $h->{date} }{ $h->{name} } = $h;

foreach my $name ( sort keys %hash ) { 
    my $name_hash = $hash{$name};
    foreach my $date ( keys %$name_hash ) { 
        print "\$hash{$name}{$date} => " . Dumper( $name_hash->{$date} ) . "\n";
    }
}

For arbitrary levels, you may want a traversal function

sub traverse_hash (&@) { 
    my ( $block, $hash_ref, $path ) = @_;
    $path = [] unless $path;
    my ( @res, @results );
    my $want           = wantarray;
    my $want_something = defined $want;

    foreach my $key ( %$hash_ref ) { 
        my $l_path = [ @$path, $key ];
        my $value  = $hash_ref->{$key};
        if ( ref( $value ) eq 'HASH' ) { 
            @res = traverse_hash( $block, $value, $l_path );
            push @results, @res if $want_something && @res;
        }
        elsif ( $want_something ) {
            @res = $block->( $l_path, $value );
            push @results, @res if @res;
        }
        else { 
            $block->( $path, $value );
        }
    }
    return unless $want_something;
    return $want ? @results : { @results };
}

So this does the same thing as above:

traverse_hash {
    my ( $key_path, $value ) = @_;
    print( '$hash{' . join( '}{', @$key_path ) . '} => ' . ref Dumper( $value ));
    ();
} \%hash
;


Perl Solution


#!/usr/bin/perl -w

use strict;
use Data::Dumper;

   sub main{
      my %hash;
      my @keys = qw(firstname lastname age);                    # hash's keys
      
      
                  #  fname    lname    age
                  # --------|--------|-----
      my @arr  = ( [ 'foo1',  'bar1',  '1' ],
                   [ 'foo2',  'bar2',  '2' ],
                   [ 'foo3',  'bar3',  '3' ]
                 );
                 
      # test if array set up correctly
      print "\$arr[1][1]       : $arr[1][1] \n";                # bar2      

    
      # loads the multidimensional array into the hash
      for my $row (0..$#arr){
         for my $col ( 0..$#{$arr[$row]} ){
            my $itemnum = "item" . ($row+1);                    # using the item# format you used
            $hash{$itemnum}->{$keys[$col]} = $arr[$row][$col];
         }
      }
      
      # manually add a 4th item
      $hash{item4} = {"firstname", "foo", "lastname", "bar", "age", "35"};
      
      
      
      # How to Retrieve
      # -----------------------
      
      # single item pull
      print "item1->firstname : $hash{item1}->{firstname} \n";  # foo1
      print "item3->age       : $hash{item3}->{age}       \n";  # 3

      # whole line 1
      {  local $, = " "; 
         print "full line        :" , %{$hash{item2}} , "\n";   # firstname foo2 lastname bar2 age 2 
      } 
      
      # whole line 2
      foreach my $key (sort keys %{$hash{item2}}){
         print "$key   : $hash{item2}{$key} \n";
      }
      
      
      # Clearer description
      #print "Hash:\n", Dumper %hash;
   }
   
   main();

This should be used in addition to the accepted answer. Your question was a little vague on the array to hash requirement, perhaps this is the model you are looking for?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜