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: Intermediate:
Modify hash

 



zapzap
User

Oct 6, 2013, 1:12 PM

Post #1 of 9 (1481 views)
Modify hash Can't Post

Greetings. I'm trying to reverse a hash so that the keys become values and values become keys. But...... I want the keys that have matching values with other keys to become an anonymous array. For example:

%h1 = { k => "dog", j => "cat", l => "dog" }

would yield

%h2 = { "dog" => [k,l], "cat" => j}

For the most part, I've already completed the code, so what I'm asking is how you would 'clean' my attempt up, or offer a different approach. Thank you very much all.


Code
sub uniq { 
my %seen;
return grep { !$seen{$_}++ } @_;
}

&uniq(values %os);
my %hash;
my $temp;

for my $val ( &uniq(values %os) ) {
$temp = [];
for my $k ( keys %os ) {
if( $os{$k} eq $val ) {
push @$temp, $k;
}
}
$hash{$val} = $temp;
}


Note: I had to use my own uniq function because I am using version 5.10


BillKSmith
Veteran

Oct 6, 2013, 3:24 PM

Post #2 of 9 (1478 views)
Re: [zapzap] Modify hash [In reply to] Can't Post

Checkout the module Hash::MoreUtils
Good Luck,
Bill


zapzap
User

Oct 6, 2013, 3:31 PM

Post #3 of 9 (1477 views)
Re: [BillKSmith] Modify hash [In reply to] Can't Post

Thanx for the advice. I will check it out. But I still would like to improve this without a module. Maybe using grep?


BillKSmith
Veteran

Oct 6, 2013, 8:32 PM

Post #4 of 9 (1471 views)
Re: [zapzap] Modify hash [In reply to] Can't Post

Your first call to uniq does not do anything useful.

The & on a subroutine call is seldom needed. Do not use it where it is not needed. In this case you really do not need uniq at all.

I like your suggestion of grep.


Code
use strict; 
use warnings;
use Data::Dumper;
my %h1 = (
k => "dog",
j => "cat",
l => "dog",
);

my %h2 = %{invert_hash(\%h1)};
print Dumper \%h2;

sub invert_hash {
my %os = %{$_[0]};
my %hash;
for my $val ( values %os ) {
next if exists $hash{$val};
$hash{$val} = [grep {$os{$_} eq $val} keys %os]
}
return \%hash;
}

Good Luck,
Bill


2teez
Novice

Oct 7, 2013, 12:44 AM

Post #5 of 9 (1466 views)
Re: [zapzap] Modify hash [In reply to] Can't Post

I would rather do this instead.

Code
use warnings; 
use strict;
use Data::Dumper;

my %hash1 = (
i => 'dog',
j => 'cat',
k => 'dog',
);

my %hash2;

for ( keys %hash1 ) {
if ( exists $hash2{ $hash1{$_} } ) {
if ( !ref $hash2{ $hash1{$_} } ) {
$hash2{ $hash1{$_} } = [ $hash2{ $hash1{$_} } ];
}
push @{ $hash2{ $hash1{$_} } }, $_;
}
else {
$hash2{ $hash1{$_} } = $_;
}
}

print Dumper \%hash2;



BillKSmith
Veteran

Oct 7, 2013, 10:19 AM

Post #6 of 9 (1457 views)
Re: [2teez] Modify hash [In reply to] Can't Post

Great idea. There is little doubt that your algorithm is much faster than either of the others. Even more important, it raises the issue of what to do when the original value is unique. Should the inverse be the value of the corresponding key or a reference to an array containing only one element, the value of that key.

Can anyone suggest a good reason to favor either one?
Good Luck,
Bill


Zhris
Enthusiast

Oct 7, 2013, 10:42 AM

Post #7 of 9 (1453 views)
Re: [BillKSmith] Modify hash [In reply to] Can't Post

I favour using an array reference containing only one element. My reason is that we can treat every value as an array when processing the new hash, and not have to ref test each value to decide how to handle (again).

Although, it depends what the data is for. If serializing via i.e. XML::Simple XMLout(), the resultant structure may vary as per which choice was made (untested).

Chris


2teez
Novice

Oct 7, 2013, 11:52 AM

Post #8 of 9 (1445 views)
Re: [BillKSmith] Modify hash [In reply to] Can't Post


Quote
There is little doubt that your algorithm is much faster than either of the others

I have not Benchmark any of the algorithm to tell which is faster, but why would I want to "grep" for every value ?

Quote
Should the inverse be the value of the corresponding key or a reference to an array containing only one element, the value of that key.

Bill, there is no one size fit all solution as you know. The solution given was in respect to the dataset shown by the OP.
Thanks.


Zhris
Enthusiast

Oct 7, 2013, 3:01 PM

Post #9 of 9 (1434 views)
Re: [2teez] Modify hash [In reply to] Can't Post


Quote
2teez
The solution given was in respect to the dataset shown by the OP.


Although the OP described yielding { "dog" => [k,l], "cat" => j}, their code actually yields { "dog" => [k,l], "cat" => [j]}. This led to which output is best as oppose to which is right, after all the OP didn't suggest their code doesn't work the way they would like.

I found this of interest, since I commonly face this "dilemma". If there is alot of data and mostly single values / only one or two multiple values then I might use a combination of arrays and strings, since in this case it would be inefficient to treat every value as an array. If the other way round, then I would probably use all arrays, since it wouldn't be much harm to treat the one or two single values as an array. With a small dataset, this isn't of concern.


Quote
2teez
but why would I want to "grep" for every value


You are right, Bill's approach is not the best, but fulfills the OP's suggestion "maybe using grep" and provides "a different approach".

Your approach is very nicely thought out, and is probably the most ideal. My personal preference would be; a while loop is more suitable over a for loop in this case; I prefer to read if statements at the end of lines where possible; I love to use loop controls...


Code
my %hash2;  
while (my ($key, $val) = each %hash1)
{
if (exists $hash2{$val})
{
$hash2{$val} = [ $hash2{$val} ] if (!ref $hash2{$val});
push @{$hash2{$val}}, $key;
next;
}
$hash2{$val} = $key;
}
print Dumper \%hash2;


If all arrays are acceptable, it can be done in 1 line...


Code
my %hash2;  
push @{$hash2{$hash1{$_}}}, $_ for (keys %hash1);
print Dumper \%hash2;


Chris

 
 


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

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