leocharre has asked for the wisdom of the Perl Monks concerning the following question:

Ok.. I have a hashref..

my $h = { mm => 'July', yyyy => '1975', dd => '31', name => 'Milo', last_name => 'Manara', };

And I want to rename some of the keys. So that I end up with:

my $h = { month => 'July', year => '1975', day => '31', first_name => 'Milo', last_name => 'Manara', };

What's a classy way to do that? something using tr// ?? I'm at a loss on how, without building another structure..

Replies are listed 'Best First'.
Re: can i rename hashref keys?
by moritz (Cardinal) on Nov 29, 2007 at 17:01 UTC
    Actually hash keys are read only (that's the point: you can only successfully build a hash table if the hash function always returns the same value).

    But you can delete pairs, and add them with a different key:

    my %name_changes = ( mm => 'month', yyyy => 'year', ); for (keys %name_change){ $h->{$name_change{$_}} = $h->{$_}; delete $h->{$_}; }

    You have to decide for yourself if that's worth the effort, or if you just want to rebuild the hash anew.

      Given that delete returns the deleted value, this can be simplified:
      my %map = (....); $h->{$map{$_}} = delete $h->{$_} for keys %map;

      Dave.

        Given that delete can delete a slice, this can be simplified:

        my %map = (...); @$h{values %map} = delete @$h{keys %map};

        Update: note that this doesn't break on %map = (foo => 'bar', bar => 'foo') that the solutions above may do.

        lodin

        and even more concisely, using a hash slice:

        @{$h}{qw{month year day first_name}} = delete @{$h}{qw{mm yyyy dd name +}}; C:\@Work\Perl>perl -wMstrict -e "my $h = { mm => 'July', yyyy => '1975', dd => '31', name => 'Milo', l +ast_name => 'Manara',}; print qq(o/p: \n); print qq($_ => $h->{$_} \n) for keys %$h; @{$h}{qw{month year day first_name}} = delete @{$h}{qw{mm yyyy dd name +}}; print qq(-----\n); print qq($_ => $h->{$_} \n) for keys %$h" o/p: mm => July name => Milo dd => 31 last_name => Manara yyyy => 1975 ----- month => July day => 31 first_name => Milo year => 1975 last_name => Manara
Re: can i rename hashref keys?
by kyle (Abbot) on Nov 29, 2007 at 17:22 UTC

    My inclination is to do it manually. That is,

    $h->{first_name} = $h->{name}; delete $h->{name};

    It's clear what it's doing, and if you're trying to change 'foo' to 'bar' and 'baz' to 'foo', it's clear if you got them in the wrong order.

    But lets say you have many many keys, and you don't want to have a huge block of those copy/delete pairs. You could do something like this:

    my $h = { mm => 'July', yyyy => '1975', dd => '31', name => 'Milo', last_name => 'Manara', }; my %replacement_for = ( mm => 'month', yyyy => 'year', dd => 'day', name => 'first_name', ); my @array_from_hash = %$h; my $x = 0; for ( @array_from_hash ) { if ( $x++ % 2 == 0 && exists $replacement_for{$_} ) { $_ = $replacement_for{$_}; } } my $h2 = { @array_from_hash }; print Dumper $h; print Dumper $h2; __END__ $VAR1 = { 'mm' => 'July', 'name' => 'Milo', 'dd' => '31', 'last_name' => 'Manara', 'yyyy' => '1975' }; $VAR1 = { 'month' => 'July', 'day' => '31', 'year' => '1975', 'last_name' => 'Manara', 'first_name' => 'Milo' };

    This avoids the problem you get if one of your replacements is one of your original keys. It also avoids accidentally creating a hash pair where there wasn't one before (e.g., if your replacement list contains an element that isn't in the original hash). Both of these are things that I think could happen if you use the type of solution given by moritz and dave_the_m (but I haven't confirmed that).

    On the other hand, it's not exactly elegant.

Re: can i rename hashref keys?
by roboticus (Chancellor) on Nov 29, 2007 at 18:26 UTC
    leocharre:

    I almost got it figured out. It's not an in-place transformation, as I was hoping to create. Actually, I was hoping to invent the Roboticus transform, but this'll have to do! ;^)

    #!/usr/bin/perl -w use strict; use warnings; use Data::Dumper; # original hash my $h = { mm => 'July', yyyy => '1975', dd => '31', name => 'Milo', last_name => 'Manara', }; print Dumper($h); my $x = { mm=>'month', dd=>'day', yyyy=>'year' }; my $t; @$t{map{$$x{$_}||$_}keys %$h) = values %$h; print Dumper($t);
    When run on my box, it gives:

    NBKI44V@B000F1FA4379F /Work/Tools/DataMap $ ./rehash2.pl $VAR1 = { 'mm' => 'July', 'name' => 'Milo', 'dd' => '31', 'last_name' => 'Manara', 'yyyy' => '1975' }; $VAR1 = { 'month' => 'July', 'day' => '31', 'name' => 'Milo', 'year' => '1975', 'last_name' => 'Manara' };

    Update:Drat! While experimenting and composing this response, several enterprising perl monks found several much better solutions.

    ...roboticus