[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
Solved

# values in hashes

Posted on 2009-04-20
Medium Priority
190 Views
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! :)
0
Question by:cucugirl
• 12
• 8
• 5

LVL 39

Expert Comment

ID: 24186139
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);
``````
0

LVL 10

Expert Comment

ID: 24186375
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;
}
``````
0

Author Comment

ID: 24186465
question for the last solution.. what is \$value going to be? in line 19?
0

Author Comment

ID: 24186566
also how would i call this subroutine?
0

Author Comment

ID: 24187046
when i run the second subroutine... it says \$value in line 19 is not defined :'(
0

LVL 10

Expert Comment

ID: 24187248
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;
``````
0

Author Comment

ID: 24187289
oleber question.. can you assing this to a hash? the return from the subroutine?
0

LVL 10

Expert Comment

ID: 24187320
like this ? ? ?

my %new_hash = %{clean_hash_by_copy(\%hash, \@array)};
0

Author Comment

ID: 24187359
%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?

0

Author Comment

ID: 24187367
ok thanks oleber.. jus tried it like that but i have this error now.. im wondering why...
0

LVL 10

Expert Comment

ID: 24187444
use

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

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

0

Author Comment

ID: 24187507
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..
0

LVL 39

Expert Comment

ID: 24187661
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);

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{\$_};
}
``````
0

LVL 39

Expert Comment

ID: 24187679
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.
0

Author Comment

ID: 24187846
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
0

LVL 10

Expert Comment

ID: 24187955
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.
0

LVL 39

Expert Comment

ID: 24188183
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);

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};
}
``````
0

LVL 10

Expert Comment

ID: 24188210
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.
0

Author Comment

ID: 24188269
should I copy before using this one?
0

LVL 10

Expert Comment

ID: 24188357
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.
0

LVL 39

Expert Comment

ID: 24188512
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);

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;
}
``````
0

Author Comment

ID: 24188606
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;
}
0

Author Comment

ID: 24188619
\$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
0

LVL 10

Accepted Solution

oleber earned 2000 total points
ID: 24190997
I didn't change the functions and I get the results in the Snippet

Your problems seem to be strange. Can you send me the real data? If you feel more comfortable, send your data to "oleber at gmail dot com".

``````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=>'up',
6=>'N'
);

my @array = (4, 'up');

my %new_hash = %{clean_hash_by_copy(\%hash, \@array)};

print "1: " . Dumper \%hash;
#1: \$VAR1 = {
#          '6' => 'N',
#          '1' => 2,
#          '3' => 4,
#          '5' => 'up'
#        };

print "2: " . Dumper clean_hash_by_copy(\%hash, \@array);
#2: \$VAR1 = {
#          '3' => 4,
#          '5' => 'up'
#        };

print "3: " . Dumper \%hash;
#3: \$VAR1 = {
#          '6' => 'N',
#          '1' => 2,
#          '3' => 4,
#          '5' => 'up'
#        };

clean_hash(\%hash, \@array);
print "4:" . Dumper \%hash;
#4: \$VAR1 = {
#          '3' => 4,
#          '5' => 'up'
#        };
``````
0

Author Comment

ID: 24193329
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 :)
0

## Featured Post

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I've just discovered very important differences between Windows an Unix formats in Perl,at least 5.xx.. MOST IMPORTANT: Use Unix file format while saving Your script. otherwise it will have ^M s or smth likely weird in the EOL, Then DO NOT use mâ€¦
I have been pestered over the years to produce and distribute regular data extracts, and often the request have explicitly requested the data be emailed as an Excel attachement; specifically Excel, as it appears: CSV files confuse (no Red or Green hâ€¦
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: Tâ€¦
Six Sigma Control Plans
###### Suggested Courses
Course of the Month20 days, 13 hours left to enroll