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

Hello,

I have the following pseudo code, and I need to build a hash of unique keys, with arrays of unique values for each key.

#!/usr/bin/perl -w use strict; my &hash; while (<DATA>) { $hash{$first} = $second; # only grabs the last value } while ((my $key, my $value) = each %construct) { print "The $key is: $value \n"; } _DATA_ 5 25 6 27 5 24 5 23 6 29 6 30 4 22 5 25 6 27 4 22 4 21
This is what I want to end up with:
The Key is: 5 25 24 23 The Key is: 6 27 29 30 The Key is: 4 22 21

Any suggestions for a routine?

Replies are listed 'Best First'.
Re: Unique keys for multiple values with a Hash
by Ovid (Cardinal) on Jan 14, 2003 at 19:08 UTC

    My approach, to ensure uniqueness in values, would be to make the values the keys of hash and then transform them into an array reference.

    #!/usr/bin/perl -w use strict; use Data::Dumper; my %hash; while (<DATA>) { next unless /[^[:space:]]/; # make sure there's something on the l +ine my ($key,$value) = split; $hash{$key}{$value} = 0; } foreach my $key (keys %hash) { $hash{$key} = [keys %{$hash{$key}}]; } print Dumper \%hash; __DATA__ 5 25 6 27 5 24 5 23 6 29 6 30 4 22 5 25 6 27 4 22 4 21

    Update: Everyone seems to want to push the value on an array which doesn't get the "unique values" in the original request.

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil (feel free to copy and distribute widely - note copyright text)

      Thanks, Ovid.

      That's exactly what I was looking for!
      Thanks again

Re: Unique keys for multiple values with a Hash
by gjb (Vicar) on Jan 14, 2003 at 19:02 UTC
    my %hash; while (<DATA>) { chomp($_); my ($first, $second) = split(/s+/, $_); push(@{$hash{$first}}, $second); } foreach my $key (keys %hash) { print "The $key is: ", join(', ', @{$hash{$key}}), "\n"; }
    should do what you want. The list references that are the value of the hash are instantiated autmagically if necessary.

    Hope this helps, -gjb-

    Update: Sorry, missed the uniqueness requirement. Since a number of good solutions have already been proposed I'll just give an alternative one. (I love sets.)

    use Set::Scalar; my %hash; while (<DATA>) { chomp($_); my ($first, $second) = split(/s+/, $_); if (!exists $hash{$first}) { $hash{$first} = new Set::Scalar(); } $hash{$first}->insert($second); } foreach my $key (keys %hash) { print "The Key is: $key\n\t", join("\n\t", $hash{$key}->members()), "\n"; }
Re: Unique keys for multiple values with a Hash
by Zaxo (Archbishop) on Jan 14, 2003 at 19:09 UTC

    The sigil for hash is %hash. &hash is an error.

    Since a hash value must be a scalar, you need to make them array references.

    while (<DATA>) { my ($key, $val) = split; push @{$hash{$key}}, $val; }
    See tye's References quick reference.

    After Compline,
    Zaxo

Re: Unique keys for multiple values with a Hash
by tall_man (Parson) on Jan 14, 2003 at 19:12 UTC
    The idea is to push values into array references in the hash. You have a few other typos I have corrected. The following should run:
    use strict; my %hash = (); my ($first, $second); while (<DATA>) { ($first, $second) = split; push (@{$hash{$first}}, $second); # from Perl Cookbook 5.7 } while ((my $key, my $value) = each %hash) { print "the Key is: $key\n"; print join("\n",@$value),"\n\n"; } __END__ 5 25 6 27 5 24 5 23 6 29 6 30 4 22 5 25 6 27 4 22 4 21
      The problem that I'm having is that all the values are being put on the array of values, and I just need the unique values I.E.
      the Key is: 4 22 21 the Key is: 5 25 24 23 the Key is: 6 27 29 30
        Sorry, I missed that the first time (as did some others). Here is a quick fix. Replace the final loop with this:
        while ((my $key, my $value) = each %hash) { print "the Key is: $key\n"; my %seen = (); # Perl Cookbook 4.6 Extracting Unique Elements from a List my @uniq = grep { ! $seen{$_}++ } @$value; print join("\n",@uniq),"\n\n"; }
        Update: I see that Ovid also has a good one-pass solution.