Link to home
Start Free TrialLog in
Avatar of cucugirl
cucugirl

asked on

values in hashes

guys if i have a value for a hash that does not match any element in an array.. can i eliminate both the value and the key for that element?

can someone do a small example for me!??! thanks! :)
Avatar of Adam314
Adam314

You can use the delete built-in function.  Example:
use Data::Dumper;
 
my %hash;
$hash{a} = 'This is a';
$hash{b} = 'This is b';
$hash{c} = 'This is c';
 
print Dumper(\%hash);
 
delete($hash{b});
 
print Dumper(\%hash);

Open in new window

you have there 2 ways, one creating a new hash, and another deleting the elements.

I optimize a litle, creating a hash from your array to fast access. you can use the grep to do the some but may be slower, much slower depending of the element number.

$array_values{$value}
can be changed to:
grep {$_ eq $value} @$array
# create a new hash 
sub clean_hash_by_copy {
    my ($hash, $array) = @_;
    my %ret_hash;
    my %array_values = map {$_=>1} @$array;
    
    while (my ($key, $value) = values %$hash) {
        $ret_hash{ $key } = $value if $array_values{$value};
    }
    return \%ret_hash;
}
 
# in the hash 
sub clean_hash {
    my ($hash, $array) = @_;
    my %array_values = map {$_=>1} @$array;
    
    foreach my $key ( keys %$hash) {
        delete($hash->{$key}) if not $array_values{$value};
    }
    return $hash;
}

Open in new window

Avatar of cucugirl

ASKER

question for the last solution.. what is $value going to be? in line 19?
also how would i call this subroutine?
when i run the second subroutine... it says $value in line 19 is not defined :'(
I just cold test now.


Try the script:
use strict;
use warnings;
use Data::Dumper;
 
# create a new hash 
sub clean_hash_by_copy {
    my ($hash, $array) = @_;
    my %ret_hash;
    my %array_values = map {$_=>1} @$array;
    
    while (my ($key, $value) = each %$hash) {
        $ret_hash{ $key } = $value if $array_values{$value};
    }
    return \%ret_hash;
}
 
# in the hash 
sub clean_hash {
    my ($hash, $array) = @_;
    my %array_values = map {$_=>1} @$array;
    
    foreach my $key ( keys %$hash) {
        delete($hash->{$key}) if not $array_values{$hash->{$key}};
    }
    return $hash;
}
 
 
my %hash = (
    1=>2,
    3=>4,
    5=>6
);
 
my @array = (1..5);
 
print "1:\n";
print Dumper \%hash;
 
print "2:\n";
print Dumper clean_hash_by_copy(\%hash, \@array);
 
print "3:\n";
print Dumper \%hash;
 
print "4:\n";
clean_hash(\%hash, \@array);
print Dumper \%hash;

Open in new window

oleber question.. can you assing this to a hash? the return from the subroutine?
like this ? ? ?

my %new_hash = %{clean_hash_by_copy(\%hash, \@array)};
%ghash = clean_hash(\%ihash, \@POptions);

Reference found where even-sized list expected at COPY.pl line 42. for that line... do you guys know why this error?


ok thanks oleber.. jus tried it like that but i have this error now.. im wondering why...
use

%ghash = @{clean_hash(\%ihash, \@POptions)};

but notice that you are changing %ihash with clean_hash method.

oleber i get this error now: Not an ARRAY reference at COPY.pl line 44. let me show you what i got... @POptions comes from here: my @POptions = routine1();
sub routine1{
      my @values = ("gy","gx","tj","up", "12", NX");
      return @values;
}

and \%ihash comes from a file..  
It will be faster to turn the array that has the keys you want into a hash rather than search the array every time.  See this example:
#The @array is the array of keys you want to keep
my @array = qw(keyA keyC);
 
#The %hash is the hash you start with
my %hash = (keyA => 'This is A', keyB => 'This is B', keyC => 'This is C');
 
#This converts the @array to the hash %array_hash
my %array_hash;
@array_hash{@array} = ();
 
#This gets an array of the keys that exist in %hash,
#but do not exist in @array
my @keys_to_delete = grep {!exists($array_hash{$_})} keys %hash;
 
#This deletes those keys and values from %hash
foreach (@keys_to_delete) {
	delete $hash{$_};
}

Open in new window

If you want to make a copy of a hash, you don't need a complicated function, just use:
    my %new_hash = %hash;

If the %hash contains references, and you want to copy all of them, you can use the dclone function from the Storable module.
mm... i want to check if the values are in the array not the keys and if they are not erase both the values and the keys
The idea is more to do readable simple code.

For example the line @array_hash{@array} = (); is difficult to read, even for Perl Mongers (PM Leader in here :). The line my %array_values = map {$_=>1} @$array; takes twice the time, but is more readable.

creating the @keys_to_delete is a very good wast of memory, and you are having 2 cicles.
As to difficult to read... First time I saw it, I didn't know what it did (although I had a guess).  I made a simple test script to play with that syntax, and now do not find it difficult.  If others do, and don't like it... I'd say this is even easier to read:
    $array_hash{$_}=undef foreach (@array);

>>i want to check if the values are in the array not the keys and if they are not erase both the values and the keys
I misunderstood the requirements... here is an update for this:

#The @array is the array of values you want to keep
my @array = qw(ValueA ValueC);
 
#The %hash is the hash you start with
my %hash = (keyA => 'ValueA', keyB => 'ValueB', keyC => 'ValueC');
 
#This converts the @array to the hash %array_hash
my %array_hash;
@array_hash{@array} = ();
 
#This checks each value in %hash, and deletes the key/value pair
#if the value is not in the @array
while(my ($k, $v) = each %hash) {
	next if exists($array_hash{$v});
	delete $hash{$k};
}

Open in new window

the code

while(my ($k, $v) = each %hash) {
        next if exists($array_hash{$v});
        delete $hash{$k};
}

is not safe, you are changing the %hash.
should I copy before using this one?
take your decision, my code is simple is already as a function.
if you prefer to have a new hash (as it seems) use clean_hash_by_copy subroutine

use some other code at your risk.
When saying it's not safe.... I didn't see anywhere in the question, or any of the asker's comments, that the original hash should not be changed.  The original question stated:
>>can i eliminate both the value and the key for that element

That is what my code does.  If you want to make a copy of the hash, you can use:
    my %hash2 = %hash;
Although this (and your code) would not work if %hash contains references.  In that case, you'd want to use dclose from Storable.

In this case, cucugirl... if you want to have %hash changed (meaning, have the key/value pairs removed from %hash), use the code exactly as I posted.  If you want to have a new hash, leaving the original %hash unchanged, use this code, which creates %hash2.

#The @array is the array of values you want to keep
my @array = qw(ValueA ValueC);
 
#The %hash is the hash you start with
my %hash = (keyA => 'ValueA', keyB => 'ValueB', keyC => 'ValueC');
 
#This converts the @array to the hash %array_hash
my %array_hash;
@array_hash{@array} = ();
 
#This checks each value in %hash, and creates the same key/value pair
#if the value is in the @array
while(my ($k, $v) = each %hash2) {
        next unless exists($array_hash{$v});
        $hash2{$k} = $v;
}

Open in new window

oleber:

sub clean_hash_by_copy {
    my ($hash, $array) = @_;
    print Dumper $hash;//PRINTS my hash
    print Dumper $array;//PRINTS my array
    my %ret_hash;
    my %array_values = map {$_=>1} @$array;
   
    print Dumper \%array_values; PRINTS THIS:

$VAR1 = {
          'N' => 1,
          'up' => 1,
          '1' => 1,
          '2' => 1,
          '3' => 1,
         
        };



    while (my ($key, $value) = each %$hash) {
        $ret_hash{ $key } = $value if $array_values{$value};
   
    }
    print Dumper \%ret_hash;
    PRINTS THIS: $VAR1 = {}; do u know what could be causing this?
    return \%ret_hash;
}
$VAR1 = {'N' => 1,
          'up' => 1,
          '1' => 1,
          '2' => 1,
          '3' => 1,
         
        };

i changed the value for the orginal array for other characters but that was about it... i was just wondering why the last print command will output that
ASKER CERTIFIED SOLUTION
Avatar of oleber
oleber
Flag of Portugal image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
got it to work.. .txt files where saved as PC files rather than UNIX files. Also I fixed this part of the code:

my %array_values = map {$_=>1} @$array;
    foreach (my ($key, $value) = each %$hash) {
delete($hash->{$key}) if not $array_values{$value};

that's how it should be instead to do what i wanted :)