Reputation:
How do I pass part of a hash to a subroutine?
%list = ( 1 => {name => 'first', quantity => 2},
2 => {name => 'second', quantity => 3});
$i = 2;
#doesn't work....
check_something ( \%{list}{$i} );
sub check_something {
%local = @_;
}
#doesn't work....
check_something ( \%list, $i );
sub check_something {
my ($ref, $item) = @_
%local = %{$ref}{$item};
}
Upvotes: 2
Views: 105
Reputation: 107040
Whenever you are storing something more than scalar data into Perl variables, you should start thinking of object oriented Perl.
Take a look at the Perl Object Oriented Tutorial.
I'm going to go all object oriented on your here:
#! /usr/bin/env perl
use strict;
use warnings;
my %list;
my $list{1} = Widget->new("first", 2);
my $list{2} = Widget->new("second", 3);
my $i = "2";
check_something( $list{$i} );
Wow! It's obvious what I'm doing. I have a hash called %list
that contains objects of Widgets! My check_somthing
subroutine is taking a Widget object. It should be $list{$i}
as the argument.
But what do Widgets look like:
package Widget;
sub new {
my $class = shift;
my $self = shift;
my $name = shift;
my $quantity = shift;
my $self = {};
bless $self, $class;
$self->Name($name) if defined $name;
$self->Quantity($quantity) if defined $quantity;
return $self;
}
sub name {
my $self = shift;
my $name = shift;
if ( defined $name ) {
$self->{name} = $name;
}
return $self->{name};
}
sub Quantity {
my $self = shift;
my $quanity = shift;
if ( defined $quanity ) {
$self->{quantity} = $quantity;
}
return $self->{quantity};
}
That's pretty simple. By using objects instead of hashes of hashes, I've simplified my logic. It's easy to see that what I want to pass to my subroutine is a Widget object. In fact, the check_somthing
subroutine could be another method:
sub check_something {
my $widget = shift;
my $name = $widget->Name;
my $quanity = $widget->Quantity;
# Here be dragons...
return ???
}
Now, my code looks like this:
#! /usr/bin/env perl
use strict;
use warnings;
my %list;
my $list{1} = Widget->new("first", 2);
my $list{2} = Widget->new("second", 3);
my $i = "2";
$list{$i}->check_something;
Notice that this does many things that improve your code:
check_something
subroutine can only be used by Widgets. If you try passing an non-Widget object to check_something
, it will fail. Even better, I could have another object called Wooble
with its own check_something
subroutine, and Perl will figure out what I am really checking.For example:
my $widget = Widget->new("first", 2);
my $wooble = Wooble->new(1, "slam");
$widget->check_something;
$wooble->check_something;
Widgets and Woobles have their own check_something
method, and Perl, because it knows which object is a Widget and which object is a Wooble knows which check_something
I should run.
use strict;
is no longer broken. The use strict;
pragma catches 90% of your errors. However, when you create these complex structures by hewing them out of raw code, you can end up creating the same time of errors that use strict;
would catch but can't:For example:
%list = (
1 => {name => 'first', quantity => 2},
2 => {name => 'second', quanity => 3}
);
Do you see the error? I used quantity
in one, but then misspelled quanity
in the other. This type of error won't be caught by the compiler. However:
$list{1} = Widget->new;
$list{1}->name('first');
$list{1}->quantity(2);
$list{2} = Widget->new;
$list{2}->name('second');
$list{2}->quanity(3); #Compiler Error!
Now, the Perl compiler will catch my error because quanity
isn't merely a key to a hash reference, but is a nonexistent subroutine.
Others have already given you the technically correct answer, but if you regularly use complex data structures like hashes of hashes or hashes of lists, or lists of hashes, it's time to start working with objects. Once you get use to them, you'll find you can code a lot faster with less frustration and fewer logic issues.
Upvotes: 0
Reputation: 50637
Pass $list{$i}
to subroutine,
use strict;
check_something ( $list{$i} );
sub check_something {
my ($href) = @_;
# $href->{name}, $href->{quantity}
my %hash = %$href;
# $hash{name}, $hash{quantity}
}
Upvotes: 1
Reputation: 13792
This works:
use strict;
use warnings;
my %list = (
1 => {name => 'first', quantity => 2},
2 => {name => 'second', quantity => 3}
);
my $i = 2;
check_something ( $list{$i} );
sub check_something {
my $item = shift;
#...
}
Upvotes: 1