Transform hash key?

M

MSG

I'd like to change a hash's keys from all lower cases to upper
The code below works but it just doesn't *feel* right with %temp.
Can it be done in a short one line of code, like how map() works
on arrays?

#!/usr/bin/perl

use strict;
use warnings;

my %h = ( a=>"1x", c=>"2y", b=>"3z" );
my %temp;
foreach my $k (keys %h){
$temp{uc $k} = $h{$k};
}
%h = %temp; # %h = (A=>"1x", c=>"2y", B=>"3z")
 
A

A. Sinan Unur

I'd like to change a hash's keys from all lower cases to upper
The code below works but it just doesn't *feel* right with %temp.
Can it be done in a short one line of code, like how map() works
on arrays?

#!/usr/bin/perl

use strict;
use warnings;

my %h = ( a=>"1x", c=>"2y", b=>"3z" );
my %temp;
foreach my $k (keys %h){
$temp{uc $k} = $h{$k};
}
%h = %temp; # %h = (A=>"1x", c=>"2y", B=>"3z")

#!/usr/bin/perl

use warnings;
use strict;

my %h = (a => "1x", c => "2y", b => "3z");
%h = map { uc $_ => $h{$_} } keys %h;

use Data::Dumper;
print Dumper \%h;
 
M

MSG

A. Sinan Unur said:
#!/usr/bin/perl

use warnings;
use strict;

my %h = (a => "1x", c => "2y", b => "3z");
%h = map { uc $_ => $h{$_} } keys %h;

use Data::Dumper;
print Dumper \%h;

Thanks! It is exactly what I am looking for!
 
A

A. Sinan Unur

Thanks! It is exactly what I am looking for!

You are welcome. Glad to be of help.

However, I presume, you were not looking for my signature. Please trim
the quoted material in your replies, and don't quote signatures.

Sinan
 
U

Uri Guttman

ASU> my %h = (a => "1x", c => "2y", b => "3z");
ASU> %h = map { uc $_ => $h{$_} } keys %h;

i would use the for modifier (untested)

$h{uc $_} = delete $h{$_} for keys %h;

i expect it to be faster but only benchmark knows for sure!

uri
 
A

A. Sinan Unur

Uri Guttman said:
ASU> my %h = (a => "1x", c => "2y", b => "3z");
ASU> %h = map { uc $_ => $h{$_} } keys %h;

i would use the for modifier (untested)

$h{uc $_} = delete $h{$_} for keys %h;

i expect it to be faster but only benchmark knows for sure!

Hmmm ... yes, this is neat.

#!/usr/bin/perl

use warnings;
use strict;

use Benchmark qw( cmpthese );

my %h = map { $_ => 1 } ('aaa' .. 'zzz');

cmpthese -1, {
map => sub { %h = map { uc $_ => $h{$_} } keys %h },
for => sub { $h{uc $_} = delete $h{$_} for keys %h; },
};

D:\Home\asu1\UseNet\clpmisc> s.pl
Rate map for
map 12.4/s -- -60%
for 31.5/s 153% --

So the for loop takes about 1/3 of the time the map takes, and
this ratio remains roughly constant when the number of keys is
increased.

Nice.
 
D

DJ Stunks

A. Sinan Unur said:
#!/usr/bin/perl

use warnings;
use strict;

use Benchmark qw( cmpthese );

my %h = map { $_ => 1 } ('aaa' .. 'zzz');

cmpthese -1, {
map => sub { %h = map { uc $_ => $h{$_} } keys %h },
for => sub { $h{uc $_} = delete $h{$_} for keys %h; },
};

D:\Home\asu1\UseNet\clpmisc> s.pl
Rate map for
map 12.4/s -- -60%
for 31.5/s 153% --

So the for loop takes about 1/3 of the time the map takes, and
this ratio remains roughly constant when the number of keys is
increased.

Nice.

Would it be possible to alias the keys and edit in-place? It would be
easy to do so with the values, but I'm not sure about the keys.

Just wondering.

Tks.
-jp
 
U

Uri Guttman

ASU> my %h = map { $_ => 1 } ('aaa' .. 'zzz');

ASU> cmpthese -1, {
ASU> map => sub { %h = map { uc $_ => $h{$_} } keys %h },
ASU> for => sub { $h{uc $_} = delete $h{$_} for keys %h; },
ASU> };

this benchmark has a common problem in that you don't set up the hash
the same way for each run. the first time you call either sub, the hash
will have only upper case keys. now i doubt that uc will be faster or
slower given any mix of input case but it still is wrong. if the
transform were something whose speed was data dependent, this benchmark
would be very bogus.

the map one could be fixed just by assigning it to a different hash. the
for one would need a fresh hash each time as it modifies it in place. so
you would need to do the same work for each one and my best idea would
be to copy the %h to %h2 inside each sub and use that as the input (and
you can use it for the output of the map. also choosing a different
output hash for the map could affect speed as well.

this is just a small lesson on the difficulty of properly benchmarking
apples and apples.

uri
 
U

Uri Guttman

DS> Would it be possible to alias the keys and edit in-place? It would be
DS> easy to do so with the values, but I'm not sure about the keys.

the values function in recent perls returns aliases to the values (which
are normal perl scalars) of the hash so you can directly mung them. but
the keys aren't normal perl values and so you can't take a reference or
alias to them. you have to deal with them indirectly via the hash's
interface which is what those two solutions do.

uri
 
A

A. Sinan Unur

ASU> my %h = map { $_ => 1 } ('aaa' .. 'zzz');

ASU> cmpthese -1, {
ASU> map => sub { %h = map { uc $_ => $h{$_} } keys %h },
ASU> for => sub { $h{uc $_} = delete $h{$_} for keys %h; },
ASU> };

this benchmark has a common problem in that you don't set up the hash
the same way for each run.

I actually thought about this before posting.
the first time you call either sub, the hash will have only
upper case keys. now i doubt that uc will be faster or slower
given any mix of input case

Which is why I decided not worry about it (in this case).
but it still is wrong.

I am not sure it is that wrong.
if the transform were something whose speed was data dependent,
this benchmark would be very bogus.
Absolutely.

the map one could be fixed just by assigning it to a different hash.
the for one would need a fresh hash each time as it modifies it in
place. so you would need to do the same work for each one and my best
idea would be to copy the %h to %h2 inside each sub and use that as
the input (and you can use it for the output of the map. also choosing
a different output hash for the map could affect speed as well.

this is just a small lesson on the difficulty of properly benchmarking
apples and apples.

Do you meam I should use:

#!/usr/bin/perl

use warnings;
use strict;

use Benchmark qw( cmpthese );

my %h = map { $_ => 1 } ('aaa' .. 'zzz');

cmpthese -1, {
map => sub { my %h2 = %h; %h2 = map { uc $_ => $h{$_} } keys %h },
for => sub { my %h2 = %h; $h2{uc $_} = delete $h2{$_} for keys %h2; },
};
__END__

D:\Home\asu1\UseNet\clpmisc> s.pl
Rate map for
map 7.31/s -- -39%
for 12.0/s 64% --

Or, should I omit the my %h2 = %h in the 'map' version?

D:\Home\asu1\UseNet\clpmisc> s.pl
Rate for map
for 12.1/s -- -3%
map 12.4/s 3% --

I don't think the latter version is fair to 'for' though.

Sinan
 
U

Uri Guttman

ASU> I actually thought about this before posting.

ASU> Which is why I decided not worry about it (in this case).

still not a good idea to not deal with it. it may have worked ok for
this case but it could lead you or someone reading this to make similar
broken benchmarks.

ASU> Do you meam I should use:

ASU> #!/usr/bin/perl

ASU> use warnings;
ASU> use strict;

ASU> use Benchmark qw( cmpthese );

ASU> my %h = map { $_ => 1 } ('aaa' .. 'zzz');

ASU> cmpthese -1, {
ASU> map => sub { my %h2 = %h; %h2 = map { uc $_ => $h{$_} } keys %h },
ASU> for => sub { my %h2 = %h; $h2{uc $_} = delete $h2{$_} for keys %h2; },
ASU> };
ASU> __END__

ASU> D:\Home\asu1\UseNet\clpmisc> s.pl
ASU> Rate map for
ASU> map 7.31/s -- -39%
ASU> for 12.0/s 64% --

that would be a better and more truthful benchmark. and you should add a
control case which just does the hash copy so you could subtract that
time and get a more accurate comparison of the real code under test.

ASU> D:\Home\asu1\UseNet\clpmisc> s.pl
ASU> Rate for map
ASU> for 12.1/s -- -3%
ASU> map 12.4/s 3% --

ASU> I don't think the latter version is fair to 'for' though.

no, that is unfair.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top