in reply to Re: Tie::Hash::MultiValue Unique question
in thread Tie::Hash::MultiValue Unique question

Fellow Monks,

First of all, Thanks for replying! Much Appreciated!

Well what I want to do is to insert multiple values per hash
key. For instance for key , say 1, I want to insert
{2,3,4,5,6,2,7,8,}, in that order.
I want to avoid inserting 2 as it has already been inserted.
The standard push will not do that I guess..
Thanks!
  • Comment on Re^2: Tie::Hash::MultiValue Unique question

Replies are listed 'Best First'.
Re^3: Tie::Hash::MultiValue Unique question
by FunkyMonk (Bishop) on Apr 17, 2009 at 23:38 UTC
    You're still not being clear about what you want.

    Here's my take...

    By definition, keys in hashes are always unique. So, if you're after lists of unique things, a hash is what you want. Since you want a unique list inside a hash, you want a hash of hashes. Something like this may do:

    use Data::Dumper; my $first_level_key = 1; my @second_level_keys = (2,3,4,5,6,2,7,8); my %hash; $hash{$first_level_key}{$_}++ for @second_level_keys; print Dumper \%hash;

    You said you wanted to insert "in order". If order is important, you could sort the keys before processing them if they're numeric, as they were in your example. If you do want strictly ordered hashes, there are modules on CPAN that can do this (disclaimer, I haven't used any of them). Just google for them.

Re^3: Tie::Hash::MultiValue Unique question
by kennethk (Abbot) on Apr 17, 2009 at 21:01 UTC
    If you want multiple values associated with one hash key, the way this is done in Perl is with a hash of lists. This corresponds to storing an array reference in the value. For an introduction to hashes of lists and other related Perl data structures, see perllol.
      Hmm I see. What I have now is:

      my %edges= ();

      while (<STREAM>) {
      my($node1, $node2) = split;
      push( @{$edges{$node1}}, $node2);

      }
      Now, if the sequence is:
      1,2
      1,3
      1,4
      1,2
      1,5

      2 will be pushed again right? I want to avoid that.
      Thanks!

        Oh! You keep talking about avoiding duplicates in a hash, but your problem is actually with avoiding duplicates in an array.

        A common idiom to weed out duplicates is: (from perlfaq4)

        my %seen; my @array = grep !$seen{$_}++, LIST;

        To insert at the same time, that would be

        my %seen; my @array = grep !$seen{$_}++, @array, $new_value;

        Plug it in and get:

        my %edges; while (<STREAM>) { my($node1, $node2) = split; my %seen; @{$edges{$node1}} = grep !$seen{$_}++, @{$edges{$node1}}, $node2; }

        A faster (but more memory hungry) version would be:

        my %edges; my %seen; while (<STREAM>) { my($node1, $node2) = split; next if !$seen{$node1}{$node2}++; push @{$edges{$node1}}, $node2; }
        Something like this may do what you want:
        use warnings; use strict; use Data::Dumper; my %edges; # created empty while (<DATA>) { my ($node1, $node2) = split; # nothing to do if node1 already has a node2 in its array. next if exists $edges{$node1}{present}{$node2}; # node2 not already there: push it, mark it present. push @{ $edges{$node1}{array} }, $node2; $edges{$node1}{present}{$node2} = 1; } # transform hash to final structure. for my $node (keys %edges) { # for each node1 of hash... # presence flags no longer needed: eliminate them. delete $edges{$node}{present}; # associate each node1 directly with its node2 array ref. $edges{$node} = delete $edges{$node}{array}; } # see what we got. print Dumper \%edges; __DATA__ 1 2 1 3 1 4 1 2 1 5 2 1 1 2 2 2 1 6 2 3 1 2 2 2
        Output:
        >perl uniq_hash_value_1.pl $VAR1 = { '1' => [ '2', '3', '4', '5', '6' ], '2' => [ '1', '2', '3' ] };
Re^3: Tie::Hash::MultiValue Unique question
by ikegami (Patriarch) on Apr 17, 2009 at 23:47 UTC