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

Hi Monks, I am having some troubles with hashes. I am simply wanting to compare elements in an array to keys in a hash. When they match I want to pull out the corresponding values into a new array. Simple. The code i am using is just not matching the values to the keys, but when I print out the keys and the array, they clearly have some similarities. Please can someone help?
my %hash = map {$numbers[$_] => $values[$_]} 0 .. $#numbers; # my @keys = keys %hash; # print @keys; foreach my $thing (@array) { # print $thing; if (exists ($hash{$thing})) { push @new_array, $thing; } } print @new_array;

Replies are listed 'Best First'.
Re: hashes - finding values
by broquaint (Abbot) on Jul 03, 2003 at 13:33 UTC
    Because exists checks for the existence of keys and not values. Instead a grep might be better suited e.g
    my @array = qw/ foo bar baz quux /; my %hash = qw/ f1 baz f2 foo /; my @newarray; foreach my $thing (@array) { push @newarray, $thing if grep { $_ eq $thing } values %hash; } print "newarray - @newarray\n"; __output__ newarray - foo baz
    This isn't terrifically optimal so you may want to create another hash where the values are the keys (although the solution starts to look like finding an array intersection which is another option).
    HTH

    _________
    broquaint

      Thanks broquaint but I want to push the corresponding keys to the matching values into the new array. I think your code keeps values in the array that are present in the hash.
      my @array = qw/ f1 f2 f3 f4/; my %hash = qw/ f1 foo f2 bar/; #OUTPUT # newarray - foo bar.
      I'm not too sure how to adapt your code to do this!
        Ok, then you'll want something like ...
        my @array = qw/ foo bar baz quux /; my %hash = qw/ f1 baz f2 foo /; my @newarray; for my $k (keys %hash) { push @newarray, $k if grep { $hash{$k} eq $_ } @array; } print "newarray - @newarray\n"; __output__ newarray - f1 f2

        HTH

        _________
        broquaint

Re: hashes - finding values
by Zaxo (Archbishop) on Jul 03, 2003 at 13:43 UTC

    Can you show us your data, what you expect, and what you get? I don't understand what is going wrong for you. The code you show looks ok and should do what you seem to want. I wonder if you have some references or something in your data which have a stringification you don't expect.

    Your code can be simplified with slice initialization and grep:

    my (%hash, @new_array); @hash{@numbers} = @values; @new_array = grep {exists $hash{$_}} @array; print @new_array;
    That kind of construction treats the arrays and hashes as distinct objects rather than piles of data.

    After Compline,
    Zaxo

      My data looks like this:
      # @numbers are shortened versions of @values to make numerical compari +son easier. @keys = ('-0.9940000000', '0.1190000000', '-0.0355000000'); @values = ('-0.993999999999999999', '-0.118999999999999999', '-0.03549 +9999999999999'); @array = ('0.1190000000', -0.0355000000'); # cmopare array to the keys in the array - extract the corresponding v +alues. # desired output --- # 0.118999999999999999 -0.035499999999999999 #

        That's the problem. The shortened stringy @numbers don't provide the same keys as the long ones in @array. Hash keys are strings and '0.1190000000' ne '.119'

        After Compline,
        Zaxo

        I don't think you need to loop or grep.

        Assuming that the values you are looking up are always in the hash, then you can use the array directly in a hash slice to produce the new array of the values like this

        my %hash; @hash{ qw[-0.9940000000 0.1190000000 -0.0355000000] } = qw[ -0.993999999999999999 -0.118999999999999999 -0.035499999999999 +999 ]; print Dumper \%hash; $VAR1 = { '0.1190000000' => '-0.118999999999999999', '-0.9940000000' => '-0.993999999999999999', '-0.0355000000' => '-0.035499999999999999' }; my @array = qw[ 0.1190000000 -0.0355000000]; my @new = @hash{ @array }; print @new; -0.118999999999999999 -0.035499999999999999

        The caveat is that if any of the values in @array don't exist in the hash as keys, then you will get undef in the corresponding elements of @new.

        However, this may actually be useful as it will give you a way of detecting that you had values in @array that didn't exist as keys in %hash.

        All of that said, if your ultimate goal is to compare floating point numbers with a fugde factor for floating point representation inaccuracies, then there are probably better, numerical ways of doing this, but you would need to show us how you are using this to be sure. I'm a bit thrown by your hash associating +0.1190000000 with -0.118999999999999999 ?? Or is that a typo?


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


Re: hashes - finding values
by davorg (Chancellor) on Jul 03, 2003 at 14:11 UTC
Re: hashes - finding values
by pfaut (Priest) on Jul 03, 2003 at 13:34 UTC

    If you want the value from the hash added to the array, use push @new_array,$hash{$thing}. The code you posted pushes the key, not the value.

    90% of every Perl application is already written.
    dragonchild
Re: hashes - finding values
by hardburn (Abbot) on Jul 03, 2003 at 14:15 UTC

    A very similar question poped up yesterday: comparing array elements to hash keys. The question isn't exactly like yours, but the replies should be applicable to your question.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    Note: All code is untested, unless otherwise stated