Beefy Boxes and Bandwidth Generously Provided by pair Networks Cowboy Neal with Hat
Perl-Sensitive Sunglasses
 
PerlMonks  

How can I use the value of a scalar as the name of an array variable?

( #43860=categorized question: print w/ replies, xml ) Need Help??
Contributed by atch on Nov 29, 2000 at 10:32 UTC
Q&A  > arrays


Description:

Suppose we have a tab-separated data file like this:
fruit apples fruit oranges fruit bananas meat sausages dairy eggs dairy milk meat bacon
And we have this program:
my( @fruit, @meat, @dairy ); while (<>) # input redirected appropriately { chomp; my( $category, $item ) = split /\t/; push ???$category???, $item; # what now?? }
The idea is to push the item into the array whose name is the same as the value in $category. I don't want to have an if else statement for each array.

Answer: How can I use the value of a scalar as the name of an array variable?
contributed by runrig

One, rather direct, answer: Use symbolic references.

Another, possibly better, answer: Use a hash of arrays instead.

There are a couple issues with using symbolic references for this. One is that symbolic references only work with global variables, so you'll need to declare them with our rather than my. Another is that you'll have to turn off strict refs when accessing the reference. Note that, with this approach, the data file essentially contains variable names! One consequence of this is that if any data row contains a name not declared in your program, you will get a fatal runtime error. Another consequence, which is more of a security hole, is that you could munge your run-time environment if you have a bad data, like

ENV foo INC bar

This program illustrates the symbolic ref approach:

use strict; our( @fruit, @meat, @dairy ); while (<>) { chomp; my( $category, $item ) = split /\t/; no strict 'refs'; push @$category, $item; } print "Fruits are: @fruit\n";
The hash-of-arrays approach is safer, and arguably cleaner, though potentially more code-heavy:
use strict; my %food; while (<>) { chomp; my( $category, $item ) = split /\t/; push @{$food{$category}}, $item; } print "Fruits are: @{$food{'fruit'}}\n";
Lastly, here's a solution using the hash-of-arrays approach but preserving your desire to put each food item into an array named for the corresponding category. To keep the data clean, the category names are normalized on input, and unrecognized category names are discarded.
use strict; my( @fruit, @meat, @dairy ); my %food; $food{'fruit'} = \@fruit; $food{'meat'} = \@meat; $food{'dairy'} = \@dairy; while (<>) { chomp; my( $category, $item ) = split /\t/; $category = lc $category; # normalize next unless exists $food{$category}; # skip unknown push @{$food{$category}}, $item; } print "Fruits are: @fruit\n";
Answer: How can I use the value of a scalar as the name of an array variable?
contributed by Russ

Let's do this without soft references:

my %Categories; while (<>) { my( $category, $item ) = split /\t/, $_, 2; push @{$Categories{$category}}, $item; } print "$_\n" for sort @{$Categories{'dairy'}};
Prints:
eggs milk

If you must have the arrays as separate variables (not just as values in the %Categories hash), you could define

%Categories = ( fruit => \@fruit, meat => \@meat, dairy => \@dairy, );
Answer: How can I use the value of a scalar as the name of an array variable?
contributed by ambrus

See also Dominus's series of three articles on this: Why it's stupid to `use a variable as a variable name'.

Answer: How can I use the value of a scalar as the name of an array variable?
contributed by arturo

I'd use a different kind of data structure: a hash of anonymous arrays. That way, you can just push the new type of food onto the array and look up that array by the hash key, which will be the category of food.

Here's how the data structure looks:

my %foods = ( fruit => [ "apples", "oranges" ], dairy => [ "eggs", "milk" ], meat => [ "sausage", "bacon" ] );
my %foods; while (<DATA>) { chomp; my( $category, $item ) = split /\t/; push @{$foods{$category}} ,$item; } for my $category ( keys %foods ) { print "$category : \n"; for my $item ( @{$foods{$category}} ) { print "\t$item\n"; } print "\n"; }
Answer: How can I use the value of a scalar as the name of an array variable?
contributed by eg

An alternative method is to use eval. In the while loop, try something like this in place of the bare push:

eval "push \@$category, $item;";

Please (register and) log in if you wish to add an answer



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others browsing the Monastery: (7)
    As of 2014-04-17 22:26 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      April first is:







      Results (458 votes), past polls