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

Suppose I have a 2-dimensional hash %ManagerInCharge{$month}{$employee_number} defined below. For each month, I'd like to change the employee number key so that the lowest employee number is assigned the number 1, the second lowest is assigned 2, etc. For example, for 200611 (November 2006), I'd to change the keys "11","22","37" to "1","2", and "3", respectively. Could I do this by treating the values ManagerInCharge{month} as a list, and changing that list? Thanks again for all your help!


%ManagerInCharge = (
200610 => {
56 => "jane",
59 => "john",
69 => "joe",
100 => "frank",
},

200701 => {
23 => "abel",
11 => "gunder",
37 => "frank",
},
);
  • Comment on Changing key values, i.e. renumbering keys?

Replies are listed 'Best First'.
Re: Changing key values, i.e. renumbering keys?
by ikegami (Patriarch) on Jan 20, 2008 at 17:38 UTC
    foreach $month (%keys %ManagerInCharge) { my $month_hash = $ManagerInCharge{$month}; my @month_array = @$month_hash{ sort { $a <=> $b } keys %$month_hash }; my $i; $ManagerInCharge{$month} = { map { ++$i => $_ } @month_array }; }

    Or if you want to switch to a HoA,

    foreach $month (%keys %ManagerInCharge) { my $month_hash = $ManagerInCharge{$month}; my @month_array = @$month_hash{ sort { $a <=> $b } keys %$month_hash }; $ManagerInCharge{$month} = \@month_array; }
Re: Changing key values, i.e. renumbering keys?
by Corion (Patriarch) on Jan 20, 2008 at 15:40 UTC

    If your keys are numbers, have you considered using an array instead?

      I think what Corion is implying is that since you're squashing the second level keys to sequential indexen you could just keep them in an arrayref instead of a hash, and the element index would be the ID naturally (which makes sense; you'd just build a new parallel copy by walking over each sub-hash's values sorted by the key and push them into the new array).

      The cake is a lie.
      The cake is a lie.
      The cake is a lie.

Re: Changing key values, i.e. renumbering keys?
by jwkrahn (Abbot) on Jan 20, 2008 at 19:45 UTC
    $ perl -e' use Data::Dumper; my %ManagerInCharge = ( 200610 => { 56 => "jane", 59 => "john", 69 => "joe", 100 => "frank", }, 200701 => { 23 => "abel", 11 => "gunder", 37 => "frank", }, ); print Dumper \%ManagerInCharge; for my $hash ( values %ManagerInCharge ) { my $index = 1; for my $key ( sort { $a <=> $b } keys %$hash ) { $hash->{ $index++ } = delete $hash->{ $key }; } } print Dumper \%ManagerInCharge; ' $VAR1 = { '200610' => { '69' => 'joe', '59' => 'john', '56' => 'jane', '100' => 'frank' }, '200701' => { '11' => 'gunder', '37' => 'frank', '23' => 'abel' } }; $VAR1 = { '200610' => { '4' => 'frank', '1' => 'jane', '3' => 'joe', '2' => 'john' }, '200701' => { '1' => 'gunder', '3' => 'frank', '2' => 'abel' } };

    Update:

    Without the nested for loops:
    for my $hash ( values %ManagerInCharge ) { my $upper = keys %$hash; @{ $hash }{ 1 .. $upper } = delete @{ $hash }{ sort { $a <=> $b } +keys %$hash }; }
Re: Changing key values, i.e. renumbering keys?
by Sixtease (Friar) on Jan 20, 2008 at 17:41 UTC

    But if you don't want to use an array, then this snippet could do:

    for $hash (values %ManagerInCharge) { $i = 0; %$hash = (map {;++$i => $_} map $hash->{$_}, sort {$a <=> $b} keys + %$hash); }
    use strict; use warnings; print "Just Another Perl Hacker\n";
Re: Changing key values, i.e. renumbering keys?
by bart (Canon) on Jan 20, 2008 at 21:24 UTC
    The way you ask your question suggests to me that your data has no reason to even be in a hash at all. There should be a meaningful relation between the hash key and its value. This appears not to be the case in your case.

    As for a technical answer: just order your keys and use a hash slice to pull out the values, and stuff them in an array.

    my $hash = { 56 => "jane", 59 => "john", 69 => "joe", 100 => "frank", }; my @array = @{$hash}{sort { $a <=> $b } keys %$hash};