Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to create a hash of arrays where i read the data from input. The data is to be read in single lines where the data is split up by ::= the left side of the ::= is to be the key and the right side of the ::= to be the value stored in the array for that key. e.g. <s> ::= <blah> <r> ::= <blah2> i want <s> and <r> to be the keys and and blah stored in <s>'s array and <blah2> in <r>'s array. thanks for any help u can provide.

Replies are listed 'Best First'.
Re: Hash of Arrays
by davido (Cardinal) on Oct 12, 2003 at 19:10 UTC
    Something like:
    use strict; use warnings; use Data::Dumper; my %hash; while (my $line = <DATA> ) { chomp $line; my ( $key, $value ) = split /\s*::=\s*/, $line; if ( $key and $value ) { push @{$hash{$key}}, $value; } } print Dumper \%hash; __DATA__ A::=B C::=D A::=C

    This assumes one entry per line. If there can be more than that, you'll need to explain what delimits each entry. The conditional (if) is just a very simplistic approach toward rejecting blank lines or mismatched key/value pairs. I also added the flexibility of allowing whitespace between the key, the ::=, and the value. As you can see when you run this snippet, my solution creates what you asked for, a hash where each element is an array of values for the appropriate key.


    Dave


    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein
Re: Hash of Arrays
by pg (Canon) on Oct 12, 2003 at 19:17 UTC
    Try to make it base on this:
    use Data::Dumper; open(DATA, "<", "test.dat"); my $data = {}; while (<DATA>) { chomp; /(.*)::=(.*)/; if (!exists($data->{$1})) { $data->{$1} = []; } $data->{$1}[$#{$data->{$1}} + 1] = $2; } print Dumper($data);

      This will work, but is there a good reason not to simplify this code by using the 'push' builtin?


      $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
        actually the the reason i wanted the array is because multiple things will be stored in it...for example.... the input looks like <man> ::= <blah> <man> ::= <blah2> so, basically we can have identical keys on the left side in the input and only one string on the right. So in my hash of arrays I'd need to go through the input storing keys and if there is a duplicate key, store whats on the right side of the ::= into that key. thanks again for your help.
Re: Hash of Arrays
by jonadab (Parson) on Oct 12, 2003 at 19:15 UTC

    Why do you want to create arrays with just one value? The following code will do it...

    my %hashofarrays=map{ my($key,$value)=split/[:]{2}=/; ($key=>[$value]) }<STDIN>;

    However, I really question whether that's actually what you want. With only one value in the array, I don't see much sense in having the array. Wouldn't it be easier just to store the value directly in the hash? Or did you want to further split the value in some way, or push it onto the array so as to preserve possible previous values (update: as pg's answer guesses), or some other thing you aren't telling us about that would impact our answer? Knowing only what you've told us, the above code does what you asked, but I'm not confident it's right.


    $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
      My method doesn't store the value directly in the hash. It pushes it onto the anonymous array referenced by the value of the appropriate hash element.


      Dave


      "If I had my life to do over again, I'd be a plumber." -- Albert Einstein

        Umm, quite so. I misread your code. Sorry about that.


        $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
Re: Hash of Arrays
by Anonymous Monk on Oct 14, 2003 at 02:45 UTC
    ok i can get it to store into a hash...but what if there are leading spaces and trailing spaces for the key (on the left of the ::= and leading and trailing space to the string on the right of the ::= how can i can strip those before i store it? thanks for any help.