CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
Delete unwanted hash keys

 



hwnd
User

Jul 17, 2013, 7:37 PM

Post #1 of 14 (1436 views)
Delete unwanted hash keys Can't Post

How could I store hash keys inside a populated hash ref and use the ' ? : ' operator to either return a defaults or delete unwanted hash keys?


Code
# $ref data example  
# { file => 'file.txt', head => 1, data => 1, id => 1 }
my $ref = @_;

# (file, type, head) are the only keys I want passed.
# file is mandatory and type, head should be optional.
my $required = { file => 1, type => 1, head => 1 };

# delete unwanted keys that are not either file, type, head
delete $ref->{$_} for grep !($required->{$_}), keys %$ref;



Laurent_R
Veteran / Moderator

Jul 17, 2013, 11:38 PM

Post #2 of 14 (1430 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

Not sure to understand what you want to do, but just a couple of commen ts on you code:


Code
my $ref = @_;


This is probably not doing what you want: this stores into $ref the number of elements of the @_ array.


Code
my $required = { file => 1, type => 1, head => 1 };

Again, although valid code, this is unlikely to be what you want. This stored into the required scalar variable a reference to an anonymous hash. Given the relatively beginner level of your questions, I doubt that you want to use hash refs. Perhaps you want:

Code
my %required = ( file => 1, type => 1, head => 1 );

which creates the %required hash.

as for your question, as I said, I don't understand it, but just syntax example:


Code
my $value = defined $specific_value? $specific_value : $default_value;



hwnd
User

Jul 18, 2013, 5:20 AM

Post #3 of 14 (1420 views)
Re: [Laurent_R] Delete unwanted hash keys [In reply to] Can't Post

Sorry, I'll try to make this clearer, I was looking for a way to populate a variable with the correct and required hash keys. Here's a rough breakdown of my code.


Code
sub dump { 
my $self = shift;
my $ref = (@_ == 1 and
ref $_[0] eq 'HASH' ) ? $_[0] :
!(@_ % 2) ? +{@_} : undef;

my $required = {file => 1, type => 1, head => 1};

croak("Illegal args, pass a hash ref or even-valued list!")
if ( !@_ ) or !defined( $ref );

croak("Parameter 'file' required") if !exists $ref->{file};

delete $ref->{$_} for grep !($required->{$_}), keys %$ref;

# $self->{$_} = exists $ref->{$_} ? delete $ref->{$_} : defaults.. for @required

...do something with $ref keys/values

}



BillKSmith
Veteran

Jul 18, 2013, 6:52 AM

Post #4 of 14 (1413 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

In this case, it is easier to handle the parameters you need and ignore any others.


Code
use strict; 
use warnings;
sample_class->sample_method(
file=>'file.txt',
head=>'some_head',
data=>'some_data'
);
sample_class->sample_method(
file=>'file.txt',
head=>'some_head',
foo =>'fum'
);
sample_class->sample_method(
head=>'some_head',
data=>'some_data'
);

package sample_class;
use Data::Dumper;
sub sample_method{
my $self = shift;
if (@_ % 2) {
die "sample_method requires named parameters\n";
}
my $tmp;
%$tmp = @_;
my $ref;
@{$ref}{qw(file head data)}= (
$tmp->{file},
defined $tmp->{head} ? $tmp->{head} : 'default_head',
defined $tmp->{data} ? $tmp->{data} : 'default_data',
);
undef $tmp;
if (!defined $ref->{file}) {die "file name required in call to sample_method\n"}
print Dumper $ref;
}


Note that starting in perl v5.10.0 there is a new operator which simplifies setting defaults.


Code
$tmp->{head}  //  'default_head', 
$tmp->{data} // 'default_data',

Good Luck,
Bill


FishMonger
Veteran / Moderator

Jul 18, 2013, 6:55 AM

Post #5 of 14 (1412 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

You may want to look at using the Params::Validate module.
http://search.cpan.org/~drolsky/Params-Validate-1.08/lib/Params/Validate.pm


FishMonger
Veteran / Moderator

Jul 18, 2013, 7:59 AM

Post #6 of 14 (1405 views)
Re: [FishMonger] Delete unwanted hash keys [In reply to] Can't Post

I probably should have given an example using the module, so here it is (borrowing some of Bill's example).


Code
#!/usr/bin/perl 

use strict;
use warnings;

sample_class->sample_method(
file=>'file.txt',
head=>'some_head',
data=>'some_data'
);
sample_class->sample_method(
file=>'file.txt',
head=>'some_head',
foo =>'fum'
);
sample_class->sample_method(
head=>'some_head',
data=>'some_data'
);


package sample_class;
use Params::Validate;
use Data::Dumper;

sub sample_method {
my $self = shift;

my %params = validate(@_, {
file => 1,
head => 1,
data => 1
});

print Dumper \%params;
}


The default error handling calls Carp::confess, as shown below, but you could override that handling.
http://search.cpan.org/~drolsky/Params-Validate-1.08/lib/Params/Validate.pm#Validation_failure


Code
c:\test>perl-1.pl 
$VAR1 = {
'head' => 'some_head',
'file' => 'file.txt',
'data' => 'some_data'
};
The following parameter was passed in the call to sample_class::sample_method but was not listed in the validation options: foo at C:\test\perl-1.pl line 29.
sample_class::sample_method(undef, 'file', 'file.txt', 'head', 'some_head', 'foo', 'fum') called at C:\test\perl-1.pl line 11



BillKSmith
Veteran

Jul 18, 2013, 9:17 AM

Post #7 of 14 (1399 views)
Re: [FishMonger] Delete unwanted hash keys [In reply to] Can't Post

I was working on a module solution as you posted. I specified a few more options.

Code
use strict; 
use warnings;
sample_class->sample_method(
# valid call - Use default data
file=>'file.txt',
head=>'some_head',
);
sample_class->sample_method(
# valid call - reference to hash
{
file=>'file.txt',
head=>'some_head',
data=>'some_data'
}
);
#sample_class->sample_method(
# #Invalid parameter
# file=>'file.txt',
# head=>'some_head',
# foo =>'fum'
#);
sample_class->sample_method(
#Missing mandatory prameter
head=>'some_head',
data=>'some_data'
);

package sample_class;
use Params::Validate qw( SCALAR validate );
use Data::Dumper;
sub sample_method{
my $self = shift;
my %parms = validate(
@_, {
file => { type => SCALAR},
head => { rype => SCALAR, optional => 1, default => 'default_head'},
data => { type => SCALAR, optional => 1, default => 'default_data'},
}
);
print Dumper \%parms;
}

Good Luck,
Bill


hwnd
User

Jul 18, 2013, 10:12 AM

Post #8 of 14 (1395 views)
Re: [BillKSmith] Delete unwanted hash keys [In reply to] Can't Post

FishMonger, BillKSmith.. I have looked at 'Params::Validate' and a few others. I was trying to work around this without having to use an additional module although those will do exactly what I need.


hwnd
User

Jul 18, 2013, 9:28 PM

Post #9 of 14 (1363 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

BillKSmith, going off your code a bit. What If I did something like so?


Code
use strict;  
use warnings;

sample_class->sample_method(
file => 'file.txt',
type => 'AOH',
head => 1,
);

sample_class->sample_method( file => 'file.txt' );

sample_class->sample_method(
file => '',
type => 'AOH',
head => 1,
);

sample_class->sample_method(
file => 'file.txt',
type => '',
head => 1,
);


my %rules = ( file => 'file.txt', type => 'AOH', head => 1 );

sample_class->sample_method( \%rules );


package sample_class;
use Data::Dumper;

sub sample_method {
my $self = shift;
my ($tmp, $ref);

@_ = ( @_ == 1 and
ref $_[0] eq 'HASH' ) ? $tmp = $_[0] :
!(@_ % 2) ? $tmp = +{@_} : undef;

@{$ref}{qw(file type head)} = (
$tmp->{file},
defined $tmp->{type} ? $tmp->{type} : 'AOH',
defined $tmp->{head} ? $tmp->{head} : 1,
);
undef $tmp;

croak("Illegal arg! Argument 'file' is required")
if !defined $ref->{file} or !$ref->{file};

croak("Illegal arg! Argument 'type' needs a structure type!")
if !$ref->{type};

print Dumper $ref;
}

__OUTPUT__
$VAR1 = {
'head' => 1,
'file' => 'file.txt',
'type' => 'AOH'
};

$VAR1 = {
'head' => 1,
'file' => 'file.txt',
'type' => 'AOH'
};

Illegal arg! Argument 'file' is required at test.pl line 14

Illegal arg! Argument 'type' needs a structure type! at test.pl line 14

$VAR1 = {
'head' => 1,
'file' => 'file.txt',
'type' => 'AOH'
};



(This post was edited by hwnd on Jul 18, 2013, 9:39 PM)


BillKSmith
Veteran

Jul 19, 2013, 7:10 AM

Post #10 of 14 (1356 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

I have several comments. Remember, after correctness, the most important property of a program is that people (including yourself) can understand it. Use of the module is a clear winner. You have not even tried to make a case for do-it-yourself.

Your test cases do pass. This suggests that your code is correct, and that you understand it (at least today!). There a few issues which you have not tested.

Use of the default values in your method calls makes it impossible to tell if the program got the correct value from the call or from the default.

You did not test any invalid parameters. Should you ignore them or croak? (Croak is the default for the module, but it can be told to ignore them.)

Good practice requires you to test combinations of these conditions. I understand that this post is not the proper place for that.

This statement is a challenge for any perl programmer.


Code
@_ = ( @_ == 1 and  
ref $_[0] eq 'HASH' ) ? $tmp = $_[0] :
!(@_ % 2) ? $tmp = +{@_} : undef;


The real purpose is the side-effect "$tmp = .....". (The final result is stored in @_ and never used. Perhaps a good thing, because many of us do not know the result of assigning a scalar to an existing array.) Note: You must study the rest of the program before you can tell the purpose of the statement.

Do you really believe that you will remember all the precedence and associativity rules needed to understand what "? $tmp = ..... " means? I did not.

Why did you not use the 'defined or' (//) operator? Are you using a version of perl older than v5.10.0?
Good Luck,
Bill


FishMonger
Veteran / Moderator

Jul 19, 2013, 8:04 AM

Post #11 of 14 (1353 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

When using compound ternary operators, you should make sure you lineup the conditionals correctly to make it easier understand the flow.


Code
   @_ = ( @_ == 1 and ref $_[0] eq 'HASH' ) ? $tmp = $_[0] 
: !(@_ % 2) ? $tmp = +{@_}
: undef;


Personally, I'd separate out the check for the number of passed in args and I probably wouldn't even use a compound ternary statement.

I'm still trying to decide what other portions I should comment on.


hwnd
User

Jul 22, 2013, 6:08 PM

Post #12 of 14 (1320 views)
Re: [BillKSmith] Delete unwanted hash keys [In reply to] Can't Post

BillKSmith, FishMonger.. I have looked into hash validation and code correction and this is how I corrected and completely understand my code.

I have prepared many cases for validation pass. First I store my required/allowed/default hash values into $opts. Next I check @_, testing whether the passed argument is a hash ref or even-valued list or croak if neither one are passed. If not @_ then croak on no passed arguments.

Next I check if 'file' key is passed because its required, if not then croak on it.

I then use grep with an @array to check for invalid arguments passed.

Finally I loop through $opts keys and check if $args exist first then checking whether the value is allowed or not, or set the default values and return $args.


Code
use strict; 
use warnings;

foo->dump(file => 'file.txt'); # ok! sets the defaults ( type => 'aoh', head => 1 )
foo->dump(file => 'file.txt', type => 'hoa', head => 1); # ok! sets these values
foo->dump(file => 'file.txt', type => 'abc', head => 1); # croaks, not an allowed value in type
foo->dump(file => 'file.txt', type => 'hoa', head => 2); # croaks, not an allowed value in head
foo->dump(type => 'hoa', head => 1); # croaks, file does not exists
foo->dump(file => 'file.txt', id => 1, data => 1); # croaks, (id,data) invalid arguments




Code
package foo; 
use Carp;

sub dump {
my $self = shift;
my $args;

my $opts = {
file => 1,
head => {
default => 1,
allowed => [0,1],
},
type => {
default => 'aoh',
allowed => [qw(aoh hoa hoh aoa)]
},
};

if (@_) {
if (ref $_[0] eq 'HASH') {
$args = $_[0];
} elsif (@_ % 2 == 0) {
%$args = @_;
} else {
croak("Illegal arg: pass a hashref, or even-valued list!");
}
} else {
croak("No arguments specified!");
}

croak("Argument 'file' is required!") unless exists $args->{file};

if ( my @junk = grep { not exists $opts->{$_} } keys %$args ) {
croak("Unknown arguments (" . (join ", ", @junk) . ")") if @junk;
}

for my $k ( keys %$opts ) {
if ( exists $args->{$k} ) {
if (my $allow = $opts->{$k}->{allowed}) {
croak("Value '$k': '$args->{$k}' not allowed!")
unless grep { $args->{$k} eq $_ } @$allow;
}
} else {
$args->{$k} = $opts->{$k}->{default};
}
}

use Data::Dumper;
print Dumper $args;
}



BillKSmith
Veteran

Jul 22, 2013, 8:16 PM

Post #13 of 14 (1315 views)
Re: [hwnd] Delete unwanted hash keys [In reply to] Can't Post

Looks goodSmile But why did you insist on reinventing the wheel? The module is much easier to use and already tested.
Good Luck,
Bill


hwnd
User

Jul 22, 2013, 8:23 PM

Post #14 of 14 (1313 views)
Re: [BillKSmith] Delete unwanted hash keys [In reply to] Can't Post

I agree the module is perfect for my case, I don't know maybe I just wanted to do it myself? ;)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives