Re: How can I use the value of a scalar as the name of an array variable?
by runrig (Abbot) on Nov 29, 2000 at 21:59 UTC
|
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";
| [reply] [d/l] [select] |
Re: How can I use the value of a scalar as the name of an array variable?
by Russ (Deacon) on Nov 30, 2000 at 00:55 UTC
|
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,
);
| [reply] [d/l] [select] |
Re: How can I use the value of a scalar as the name of an array variable?
by ambrus (Abbot) on Mar 30, 2012 at 15:49 UTC
|
| [reply] |
|
| [reply] |
Re: How can I use the value of a scalar as the name of an array variable?
by arturo (Vicar) on Nov 30, 2000 at 00:10 UTC
|
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";
}
| [reply] [d/l] [select] |
Re: How can I use the value of a scalar as the name of an array variable?
by eg (Friar) on Dec 03, 2000 at 15:51 UTC
|
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;";
| [reply] [d/l] |
Re: Can I determine if the value of a variable is the same as an arrays name?
by jreades (Friar) on Nov 29, 2000 at 21:16 UTC
|
You can do this using symbolic references, but running under -w or use strict this will often generate a number of warnings.
Here's an example:
$two = 'test';
@three = ('test');
my @array = ('one', 'two', 'three');
foreach (@array) {
if (@$_) {
print "Array exists: $_\n";
}
if ($$_) {
print "Scalar exists: $_\n";
}
}
And this is how it would apply to your problem:
my @fruit = ();
my @meat = ();
my @dairy = ();
open (FOOD, "./food.txt")
while (<FOOD>) {
chomp;
($category, $item) = split(/\$/, $_);
push(@{$category}, $item) if (@{$category});
}
So yes, it's do-able, but it's not always the best programming method although it seems to fit here and wouldn't generate warnings as you've declard the arrays you're trying to assign to and not initializing them on the fly.
Originally posted as a Categorized Answer. | [reply] [d/l] [select] |
Re: Can I determine if the value of a variable is the same as an arrays name?
by extremely (Priest) on Nov 29, 2000 at 10:53 UTC
|
Learn about a Hash of Arrays:
my %HoA;
while (<>)
{
chomp;
my( $category, $item ) = split /\t/, $_, 2;
push @{$HoA{$category}}, $item;
}
for my category ( sort keys %HoA )
{
local $" = ", ";
print "$category: @{$HoA{$category}}\n";
}
| [reply] [d/l] |
|
| [reply] [d/l] |
Re: Can I determine if the value of a variable is the same as an arrays name?
by cephas (Pilgrim) on Nov 29, 2000 at 23:32 UTC
|
You could try something like this...
@fruit = ();
@meat = ();
@dairy = ();
open (FOOD, "./food.txt");
while(<FOOD>) {
chomp;
($category,$item) = split(/\s+/,$_);
push(@{"$category"},$item);
}
But of course, that all assumes you trust your input.
cephas
Originally posted as a Categorized Answer. | [reply] [d/l] |
Re: Can I determine if the value of a variable is the same as an arrays name?
by mnperez (Sexton) on Nov 29, 2000 at 14:09 UTC
|
This solution uses a hash of arrays (HoA) instead of symbolic references.
my %hoa;
while (<>)
{
chomp;
my( $category, $item ) = split /\t/;
push @{$hoa{$category}}, $item;
}
for my $cat ( sort keys %hoa )
{
print "The following are in category $cat:\n";
for ( @{$hoa{$cat}} )
{
print "\t$_\n";
}
}
Originally posted as a Categorized Answer. | [reply] [d/l] |
Re: Can I determine if the value of a variable is the same as an arrays name?
by jreades (Friar) on Nov 30, 2000 at 20:29 UTC
|
What you're looking for are symbolic (or as Russ called them, soft) references.
my( @fruit, @meat, @dairy );
while (<>)
{
chomp;
my( $category, $item ) = split /\t/;
push @{$category}, $item if defined @$category;
}
Note that this version would only push items with pre-declared categories, the rest would be silently discarded -- this might be the behaviour you want, it might not. Your call.
Update: fixed test per chipmunk's reply.
| [reply] [d/l] |
|
| [reply] [d/l] |
|
Quote:
push(@{$category}, $item) if (@{$category});
...
Note that this version would only push items with pre-declared categories, the rest would be silently discarded
I'm afraid that version will silently discard everything. @{$category} will never be true, because all of your pre-declared arrays start with zero elements.
You could do something like this:
@fruit = (undef);
@meat = (undef);
@dairy = (undef);
while (<FOOD>) {
chomp;
($category, $item) = split(' ', $_);
push(@{$category}, $item) if (defined @{$category});
}
shift @fruit;
shift @meat;
shift @dairy;
But really, why would you want to? I think it's much simpler to restrict the categories if you use a hash of lists. | [reply] [d/l] [select] |
A reply falls below the community's threshold of quality. You may see it by logging in. |