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


Dear Monks,
I had asked this question in CB yesterday but unforntunately was not able to solve the problem with the lead given. And after seeing the node for XY Problem I have repharsed my question! :) Please help me out in finding the right solution to the below problem

I have two hashes which would look as shown below
%hashA = ( 11.5 => 1723478, 1734789, 1798761 11. 12 => 1700123, 11.01 => 1780345 );

I hope that the above multi-valued key can be established with Data::Dumper and Push.
%hashB = ( 1723478 => 237, 1734789 => 114, 1798761 => 147 );

My situtaion is,
1. I have to find the max key in hashA which hopefully can be done with max keys (%hashA).
2. One specific value has to be removed from a key. For example value
1734789 has to be removed from the key 11.5 leaving the other values intact.
3. Remove the corresponding key/pair value from hashB.

My problem is with step 2: I was not able to find a simple and quick approach to remove specific values of a particular key.
Please advise me on this.

Thanks,
Eshwar.

Replies are listed 'Best First'.
Re: Hash: Removing a specific value from a multi-valued key
by Bloodnok (Vicar) on Sep 30, 2009 at 23:54 UTC
    Correcting your definition of %hashA to
    %hashA = ( '11.5' => [ 1723478, 1734789, 1798761, ], '11.12' => [ 1700123, ], '11.01' => [ 1780345, ] );
    Use:
    @{$hashA{q/11.5/}} = [ grep { $_ != 1734789 }, @{$hashA{q/11.5/}} ];
    or
    my $id = q/11.5/; @{$hashA{$id}} = [ grep { $_ != 1734789 }, @{$hashA{$id}} ];

    Update:

    Added re/correct definiiton of %hashA

    A user level that continues to overstate my experience :-))
      Hi,
      Thanks for the reply and correction.
      Unfortunately I am working on a windows Vista m/c with ActivePerl installation. Hence wont be able to use a grep command. And qgrep is also not supported.
      Is there a work around for this?

      Thanks,
      Eshwar
        Bloodnok is referring to the Perl built-in grep function, which should be available to you, not the unix grep utility. Yes, it is confusing.
Re: Hash: Removing a specific value from a multi-valued key
by Marshall (Canon) on Oct 01, 2009 at 04:06 UTC
    I agree with Bloodnok about the data structure. You have a Hash of Array. Basically this is a hash key that points to not one value, but actually a reference to a list of values.

    The q/11.5/ syntax is a type of a Perl quote and it just means '11.5'. There are a bunch of these short-cut quote operators in Perl (q(single quote),qq (double quote), qw (quote word),etc). If you are going to write more Perl, buy "Programming Perl" by Larry Wall, Tom Christiansen and Jon Orwant and look at page 63 which has a table about this "quoting" subject.

    I prefer the below synatx.

    $hashA{q/11.5/} = [ grep { $_ != 1734789 } @{$hashA{q/11.5/}} ];
    First, @{$hashA{q/11.5/}}, the q/11.5/ just means '11.5'.
    $hashA{'11.5'} points to an anonymous array of values.
    @{$hashA{q/11.5/}} expands that anonymous array into a list of values
    That list goes into grep which makes a yes/no decision for each list item.
    If the value is not 1734789 then the value gets put into yet another anonymous list. Meaning that values = 1734789 are deleted.
    The output of the grep{} is a list and the enclosing [ ] brackets around this big expression allocate memory for that list which is then assigned back to the $hashA{'11.5'} key.

    See below code.

    #!/usr/bin/perl -w use strict; use Data::Dumper; my %hashA = ( '11.5' => [ 1723478, 1734789, 1798761, ], '11.12' => [ 1700123, ], '11.01' => [ 1780345, ] ); print Dumper (\%hashA); $hashA{q/11.5/} = [ grep { $_ != 1734789 } @{$hashA{q/11.5/}} ]; print Dumper (\%hashA); __END__ Prints: $VAR1 = { '11.01' => [ 1780345 ], '11.12' => [ 1700123 ], '11.5' => [ 1723478, 1734789, 1798761 ] }; $VAR1 = { '11.01' => [ 1780345 ], '11.12' => [ 1700123 ], '11.5' => [ 1723478, 1798761 ] };
    1734789 was in the anonymous array assigned to $hashA{'11.5'} and it is no longer there.
      Hi,
      Thanks to Bloodnok, toolic and Marshall for your inputs. You ppl are amazing.
      I will try what you have suggested.

      Thanks,
      Eshwar
Re: Hash: Removing a specific value from a multi-valued key
by johngg (Canon) on Oct 01, 2009 at 12:41 UTC

    This is slightly tangential to your problem but I wonder whether you might be able to use a single HoH structure in your problem rather than two separate hashes.

    use strict; use warnings; use List::Util qw{ max }; use Data::Dumper; my %hashA = ( 11.5 => { 1723478 => 237, 1734789 => 114, 1798761 => 147, }, 11.12 => { 1700123 => 123, }, 11.01 => { 1780345 => 456, }, ); print Data::Dumper->Dumpxs( [ \ %hashA ], [ qw{ *hashA } ] ); delete $hashA{ max keys %hashA }->{ q{1734789} }; print Data::Dumper->Dumpxs( [ \ %hashA ], [ qw{ *hashA } ] );

    The output.

    %hashA = ( '11.01' => { '1780345' => 456 }, '11.12' => { '1700123' => 123 }, '11.5' => { '1734789' => 114, '1723478' => 237, '1798761' => 147 } ); %hashA = ( '11.01' => { '1780345' => 456 }, '11.12' => { '1700123' => 123 }, '11.5' => { '1723478' => 237, '1798761' => 147 } );

    I hope this is useful.

    Cheers,

    JohnGG