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

Okay, got something new for all of you that have been helping me. I have a hash the refers to an array that can contain either just a number, or a number that is a key in the hash.
ex:
$hash{"0"}=("1","2","15"); $hash{"1"}=("3","4"); $hash{"2"}=("5","6"); $hash{"3"}=("7","8");

I would like to translate it into:
$hash{"0"}=(("1","3","4","7","8"),("2","5","6"),("15"));

Any ideas on this?

==
Kwyjibo. A big, dumb, balding North American ape. With no chin.

Replies are listed 'Best First'.
Re: A little fun with hashes
by dpuu (Chaplain) on Aug 16, 2002 at 18:00 UTC
    Here's some code that works for your test data. There's an obvious bug (infinite recursion if there's a loop in your data): I'm sure you can solve that yourself.
    $hash{"0"}=["1","2","15"]; $hash{"1"}=["3","4"]; $hash{"2"}=["5","6"]; $hash{"3"}=["7","8"]; sub expand { my ($key) = @_; if (exists $hash{$key}) { return sort { $a <=> $b } $key, map { expand($_) } @{$ha +sh{$key}} } else { return $key } } my @expanded = map {[ expand $_ ]} @{$hash{0}}; use Data::Dumper; print Dumper(\@expanded);
    --Dave

    Update: I've just realised that the if (exists ...) check is not needed: if the key doesn't exist, then an empty array will be used, automagically. so the expand function becomes:

    sub expand { my ($key) = @_; return sort { $a <=> $b } $key, map { expand($_) } @{$hash{$ke +y}} }
    Update 2: For efficiency, you probably want to move the sort outside the loop!

    Update 3: For the golfers amongst us:

    sub e{@_,map{e($_)}@{@hash{@_}}} print Dumper[map{[sort{$a<=>$b}e$_]}@{$hash{0}}]
Re: A little fun with hashes
by chromatic (Archbishop) on Aug 16, 2002 at 17:40 UTC

    I think you mean to assign anonymous arrays and not lists. Other than that, I don't see your pattern. Can you be more explicit about how you decide what fits where?

    $hash{0} = [ 1, 2, 15 ]; $hash{1} = [ 3, 4 ]; $hash{2} = [ 5, 6 ]; $hash{3} = [ 7, 8 ];
      So with key = "0", we have possible keys of 1, 2, or 15. If it isn't a key, we need to keep the value. If it is a key, we still need to keep the value, but also add in the values of the hash at that key.

      Do you see now?

      Edited
      I almost forgot, the values of the item, if it is a key, also can be keys themselves, and need to be treated in the same way.
        As chromatic said, your initial assignments don't quite work. You cannot put an array ("3","4) into a scalar $hash{"1"}; instead, you need to use references.

        It sounds like you would like to do a recursive descent, expanding each index into itself as well as its values in the hash, if any. I'm not quite sure how you can do that and get exactly the string that you describe, but the following does something like what you describe.

        #!/usr/bin/perl -w $hash{"0"} = ["1","2","15"]; $hash{"1"} = ["3","4"]; $hash{"2"} = ["5","6"]; $hash{"3"} = ["7","8"]; # Recursively expand a key in the hash into itself and its values. # Returns a reference to an array of keys and references to arrays. # Warning: If $hash{$n} contains $n then this expansion won't # terminate. sub expand { my ($key) = @_; my @result = ( $key ); # Keep the value. foreach my $subkey ( @{$hash{$key}} ) { # If it's a key, expand push @result, expand($subkey); # that too and append. } return \@result; } # Apply the algorithm to find the expanded "0" entry. $newhash{0} = expand(0) ; # Pretty print the result as a perl data structure. use Data::Dumper; print Dumper( $newhash{0} ); # Like the expand() routine, # but keep everything at the same depth. sub expandFlat { my ($key) = @_; my @result = ( $key ); foreach my $subkey ( @{$hash{$key}} ) { push @result, expandFlat($subkey); } return @result; } # Print out the flattened structure. print "(". join(",",expandFlat(0)) .")\n"; # Running this and compacting the Data::Dumper output a bit gives # # (1) # As a perl data structure: # $VAR1 = [ 0, [ '1', [ '3', [ '7' ], # [ '8' ] ], # [ '4' ] ], # [ '2', [ '5' ], # [ '6' ] ], # [ '15' ] ]; # # (2) # As a flat array # (0,1,3,7,8,4,2,5,6,15) # # which as I said, is pretty much your procedure # if not quite your outcome. #
Re: A little fun with hashes
by ehdonhon (Curate) on Aug 16, 2002 at 22:00 UTC

    If you run this in a loop, you'll eventually get to a point where all the values are non-keys. But this solution assumes you are not interested in the recursion. It also assumes you want to process every key in the hash and not just "0".

    my %newhash; foreach my $key ( keys( %hash ) ) { $newhash{$key} = [ map {exists($hash{$_}) ? $hash{$_} : $_;} (valu +es %hash) ]; } %hash = %newhash;