views:

120

answers:

4

I have a source list from 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?

+1  A: 

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"}
The "easy way" is going to be the "hard way" if your data turns out to have colons in it.
Kinopiko
fortunately the data does not contain colons. Thanks much. This worked :)
pythonperl
Agreed, easy way is not advised. you'd be better use a null byte `\0` over a colon, but even that isn't suggested.
vol7ron
A: 

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'}"
Sinan Ünür
A: 

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?

vol7ron
A: 

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
;
Axeman
nice work :) Thanks much :)
pythonperl