简体   繁体   中英

Perl hashes with value as array

I have an input file like below:

start
f1="apple"
f2="banana"
f3="berry"
end
start
f1="guava"
f2="banana"
f3="berry"
end
start
f1="apple"
f2="cucumber"
f3="orance"
end

I am planning to create a hash with values as arrays.

All the tags will be in a temporary array and and in between the tags there is a field f1 whose value will be the key of the hash.

From start to end it will be stored in temporary array, and once the end is reached, the array is pushed as a value of hash with key as the value found after the latest string f1=

I have written the following code but it does not work.

Could anyone suggest any correction here?

#!/usr/bin/perl -w

use strict;

use Data::Dumper;

open( FILE, "<", "temp2" );

my %FILEDATA;
my @tmp   = ();
my $fruit = "";

while ( <FILE> ) {

    chomp( $_ );
    push @tmp, $_;

    my @linefields = split( '=', $_ );

    if ( $linefields[0] =~ /f1/ ) {
        $fruit = $linefields[1];
    }

    if ( $_ =~ /end/ ) {

        if ( $fruit eq "apple" ) {

            if ( exists $FILEDATA{"apple"} ) {
                push( @{ $FILEDATA{"apple"} }, @tmp );
            }
            else {
                $FILEDATA{"apple"} = @tmp;
            }
        }
        elsif ( $fruit eq "guava" ) {

            if ( exists $FILEDATA{"guava"} ) {
                push( @{ $FILEDATA{"guava"} }, @tmp );
            }
            else {
                $FILEDATA{"guava"} = @tmp;
            }

        }

        undef @tmp;
    }
}

print Dumper( \%FILEDATA );

I'm assuming this is homework

Here are some guidelines

  • $_ is the default parameter to many Perl operators. In particular, chomp( $_ ) may be written as chomp , split( '=', $_ ) should be split /=/ , and if ( $_ =~ /end/ ) { ... } can be if ( /end/ )

  • There is no need to test the value of $fruit . If its value is the key that you want then you should use $FILEDATA{$fruit}

  • $FILEDATA{"apple"} = @tmp sets $FILEDATA{"apple"} to the number of elements in @tmp . You need to set it to a reference to the array, with $FILEDATA{"apple"} = \\@tmp

  • There is no need to check whether a hash or array element exists before using push . If you just push @{ $FILEDATA{apple} }, 'ff' then the array will be created

  • if ( $linefields[0] =~ /f1/ ) tests whether $linefields[0] contains the string f1 . To test for equality you want if ( $linefields[0] eq 'f1' )

  • You shouldn't empty an array or a hash with undef @data or @data = undef . The correct way is @data = () . To empty a scalar, use $val = undef

Here's how your code should look

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

open FILE, "<", "fruit.txt";

my %data;
my @tmp;
my $fruit;

while ( <FILE> ) {

    chomp;
    push @tmp, $_;

    my @fields = split /=/;

    if ( $fields[0] eq 'f1' ) {
        $fruit = $fields[1];
    }

    if ( /end/ ) {
        push @{ $data{$fruit} }, [ @tmp ];
        @tmp = ();
    }
}

print Dumper( \%data );

output

$VAR1 = {
          '"guava"' => [
                         [
                           'start',
                           'f1="guava"',
                           'f2="banana"',
                           'f3="berry"',
                           'end'
                         ]
                       ],
          '"apple"' => [
                         [
                           'start',
                           'f1="apple"',
                           'f2="banana"',
                           'f3="berry"',
                           'end'
                         ],
                         [
                           'start',
                           'f1="apple"',
                           'f2="cucumber"',
                           'f3="orance"',
                           'end'
                         ]
                       ]
        };

And here's how I would write it

#!/usr/bin/perl

use strict;
use warnings;
use autodie;

use Data::Dumper;

my $file = 'fruit.txt';

open my $fh, '<', $file
    or die qq{Unable to open "$file": $!};

my ( %data, @block, $fruit );

while ( my $line = <$fh> ) {

    chomp $line;

    push @block, $line if @block or $line eq 'start';

    $fruit = $1 if $line =~ /^f1="(\w+)"/;

    if ( $line eq 'end' ) {
        push @{ $data{$fruit} }, [ @block ];
        @block = ();
        $fruit = undef;
    }
}

print Dumper \%data;

Note that you shouldn't empty an array or a hash with undef @data or @data = undef . The correct way is @data = ()

Try this code

my $file = 'fruits.txt';
my %hash;


open (my $fh, '<', $file) or die "Could not open $!";
while(my $line = <$fh>) {
  next if $line =~ /start/;
  next if $line =~ /end/;

  if ($line =~ /f1/) {
     my ($key, $val) = split /=/, $line;
     push @{$hash{$key}}, $val;
   }

   if ($line =~ /f2/) {
    my ($key, $val) = split /=/, $line;
    push @{$hash{$key}}, $val;
   }

   if ($line =~ /f3/) {
    my ($key, $val) = split /=/, $line;
    push @{$hash{$key}}, $val;
   }

}

print Dumper(\%hash);

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