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

Hello monks: My data looks something like
Honda Civic Accord Toyota Camry Corolla Tundra Nissan Maxima
I would like to have a hash key named 'Honda', 'Toyota', and 'Nissan', with values being associated with Key. For example, 'Honda' would be the key for the hash, and 'Civic', and 'Accord' would be 2 array elements in the 'Honda' hash. Any idea how I would do this? TIA

Replies are listed 'Best First'.
Re: hashes with arrays
by brian_d_foy (Abbot) on Nov 07, 2007 at 02:19 UTC

    Huh. Everyone wants to type a lot. :)

    You're trying to create a "Hash of Arrays". Check out perldsc, the "Perl Data Structure Cookbook" for lots of examples. Here's how I created it from your example data:

    #!/usr/bin/perl use strict; use warnings; my %hash = (); my $key; while( <DATA> ) { chomp; if( /^(\S.*)/ ) { $key = $1 } elsif( /^\s+(\S.*)/ ) { push @{ $hash{$key} }, $1 } elsif( /^\s*$/ ) { next; } } use Data::Dumper; print Dumper( \%hash ); __DATA__ Honda Civic Accord Toyota Camry Corolla Tundra Nissan Maxima
    --
    brian d foy <brian@stonehenge.com>
    Subscribe to The Perl Review
Re: hashes with arrays
by Fletch (Bishop) on Nov 07, 2007 at 01:03 UTC
Re: hashes with arrays
by magawake (Novice) on Nov 07, 2007 at 00:37 UTC
    Ok, I think I got it.. thanks :-)
    $ref->{car}->{Honda}->[0]="Civic"; $ref->{car}->{Honda}->[1]="Accord"; $ref->{car}->{Toyota}->[0]="Camry"; $ref->{car}->{Toyota}->[1]="Tundra"; $ref->{car}->{Toyota}->[2]="Corolla";

      Several things there that can be improved on. ;)

      The first is that $ref is a hash reference. More likely you simply want a hash:

      my %ref;

      Perl allows some syntactic sugar that removes all those -> so you can write:

      $ref{car}{Toyota}[2]="Corolla";

      However if you want to initialize a hash then you can:

      my %ref = ( car => { Honda => [qw(Civic Accord)], Toyota => [qw(Camry Tundra Corolla)], } );

      taking advantage of qw so that the car names don't require quoting.


      Perl is environmentally friendly - it saves trees

      You seem to have an extra level, a hash with a single key.

      $ref->{Honda}->[0]="Civic"; $ref->{Honda}->[1]="Accord"; $ref->{Toyota}->[0]="Camry"; $ref->{Toyota}->[1]="Tundra"; $ref->{Toyota}->[2]="Corolla";

      By the way, the arrow between indexes can be omitted.

      $ref->{Honda}[0]="Civic"; $ref->{Honda}[1]="Accord"; $ref->{Toyota}[0]="Camry"; $ref->{Toyota}[1]="Tundra"; $ref->{Toyota}[2]="Corolla";

      It seems odd to refer to array indexes using a constant.

      $ref->{Honda} = [ qw( Civic Accord ) ]; $ref->{Toyota} = [ qw( Camry Tundra Corolla ) ];

      Or if you were in a loop,

      push @{$ref->{Honda}}, 'Civic'; push @{$ref->{Honda}}, 'Accord'; push @{$ref->{Toyota}}, 'Camry'; push @{$ref->{Toyota}}, 'Tundra'; push @{$ref->{Toyota}}, 'Corolla';
Re: hashes with arrays
by runrig (Abbot) on Nov 07, 2007 at 04:05 UTC
    Depending on what you want to do with the data structure, you might want to build a hash of arrays as others have suggested. If you want to, say, validate makes and models, you might want a hash of hashes. To paraphrase some of what brian_d_foy posted above:
    #!/usr/bin/perl use strict; use warnings; my %hash; my $make; while( <DATA> ) { chomp; if( /^(\S.*)/ ) { $make = $1 } elsif( /^\s+(\S.*)/ ) { $hash{$make}{$1} = 1 } elsif( /^\s*$/ ) { next; } } use Data::Dumper; print Dumper( \%hash ); # Valid - prints "Valid..." my ($make1, $model1) = qw(Nissan Maxima); print "Valid $make1 model1\n" if $hash{$make1}{$model1}; # Not valid - doesn't print my ($make2, $model2) = qw(Toyota Mustang); print "Valid $make2 model2\n" if $hash{$make2}{$model2}; # Valid - prints "Valid..." my $make3 = "Ford"; print "Valid $make3\n" if $hash{$make3}; __DATA__ Honda Civic Accord Toyota Camry Corolla Tundra Nissan Maxima
    If dealing with makes by themselves is unnecessary, you can even use simulated multilevel hashes (saving some memory if there were a large number of makes and models) by changing this line:
    elsif( /^\s+(\S.*)/ ) { $hash{$make}{$1} = 1 }
    to this:
    elsif( /^\s+(\S.*)/ ) { $hash{$make,$1} = 1 }
    And then validate with this:
    print "Valid $make1 $model1\n" if $hash{$make1,$model1};
Re: hashes with arrays
by Anonymous Monk on Nov 07, 2007 at 01:52 UTC
    You have really horrid taste in motorcars - good god man get a life!!

      Oh it's not the OP's taste in cars. That list was supplied by the school teacher.


      Perl is environmentally friendly - it saves trees