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

The foreach () loop I am trying to rewrite as a map {} is:
  foreach (sort { $a <=> $b } keys %hash) { push @c, \@{$hash{$_}}; }
My question is:  "Is this even possible, considering what is going on in the foreach () and what I am pushing (an array ref to a two dimensional array, see read more for the gory details)?"  My gut reation is the loop can be rewritten, but the solution escapes me.

Click on read more to see the full context.



The intent of this function is to merge two, two dimensional arrays and return the merged array.  The first and second elements (array indicies 0 and 1, respectivly) are left alone in the merge because the first is text and the second is what I key off of (munging my keys would generally be considered a Bad Thing and text does not play well with mathematical operators).
sub merge { my @a; my @b; { my $a_ref = shift; my $b_ref = shift; @a = @{$a_ref}; @b = @{$b_ref}; } my %hash; my @c; for(my $x = 0; $x <= $#b; $x++) { $hash{$b[$x][1]} = \@{$b[$x]};} for(my $x = 0; $x <= $#a; $x++) { if((exists $hash{$a[$x][1]}) && (defined $hash{$a[$x][1]})) { for(my $y = 2; $y <= $#{$hash{$a[$x][1]}}; $y++) { $hash{$a[$x][1]}[$y] = ($hash{$a[$x][1]}[$y] + $a[$x][$y]) / + 2; } } else { $hash{$a[$x][1]} = \@{$a[$x]}; } } foreach (sort { $a <=> $b } keys %hash) { push @c, \@{$hash{$_}}; } return @c; }
Update: Removed prototype.

Replies are listed 'Best First'.
Re: How do I rewrite this foreach() loop as a map{} ?
by BrowserUk (Patriarch) on May 10, 2005 at 16:06 UTC

    No need for a loop at all.

    my @c = @hash{ sort{ $a <=> $b } keys %hash };

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: How do I rewrite this foreach() loop as a map{} ?
by jeffa (Bishop) on May 10, 2005 at 16:03 UTC
Re: How do I rewrite this foreach() loop as a map{} ?
by Fletch (Bishop) on May 10, 2005 at 16:00 UTC

    Erm, @c = map { [ @{ $hash{ $_ } } ] } sort { $a <=> $b } keys %hash?

    Not to mention immediately taking a reference to a dereferenced array ref gives you the same arraryref.

Re: How do I rewrite this foreach() loop as a map{} ?
by jfroebe (Parson) on May 10, 2005 at 16:00 UTC
    foreach (sort { $a <=> $b } keys %hash) { push @c, \@{$hash{$_}}; }

    maybe something like:

    @c = map { \@{ $hash{$_} } } (sort { $a <=> $b } keys %hash);

    Note that this is untested and will replace @c with the result of the map. You could append to the @c array like so:

    push @c, map { \@{ $hash{$_} } } (sort { $a <=> $b } keys %hash);

    Jason L. Froebe

    Team Sybase member

    No one has seen what you have seen, and until that happens, we're all going to think that you're nuts. - Jack O'Neil, Stargate SG-1

      Thank you both.  I feel stupid now...

Re: How do I rewrite this foreach() loop as a map{} ?
by deibyz (Hermit) on May 10, 2005 at 16:04 UTC
    I've not deeply read you're code, but that's what I see:

    First, I see you have \@{$hash{$_}} and similar contructs in your code.
    I understand that $hash{$_} is a reference to the array, and you're dereferecing and taking reference again. Why?

    I would just (using map):

    @c = map { $hash{$_} } sort keys %hash;

    I also se you're using a prototype of $$. Are you sure you know the implications?

      Which implications are you refering to?  I'm pasing in two scalars, which incedentally are references to two arrays, and immediately dereference them into real arrays.

        I recommend you read this: When to use Prototypes? and heed tilly's advice. In fact, the only time i've ever even needed to use Prototypes is with mod_perl. Me? I avoid them. But Your Milleage Will Vary and There Is More Than One Way To Do It.

        Oh yeah ... you might want to check out Math::Matrix instead of rolling your own solution. It's a pretty handy module.

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
Re: How do I rewrite this foreach() loop as a map{} ?
by Animator (Hermit) on May 10, 2005 at 17:10 UTC

    Some comments on your code (not a direct answer, but seriously some things you should be carefull with (if you want an answer to your loop-question then look at the previous posts, BrowserUk's one is a good one)

    First: $hash{$b[$x][1]} = \@{$b[$x]}; What are you tring to accomplish with the code? you are dereferencing an array-reference only to take the reference to it... It would be a lot easier to write: $hash{$b[$x][1]} = $b[$x]; which behaves exactly the same (or atleast when it really is an array-reference, if it isn't one then your code will die at some point)

    defined $hash{$a[$x][1]} it might be better to write: ref $hash{ $a[$x][1] } eq 'ARRAY' this will make sure it is an array-reference, and not for example a scalar, or a hash-reference.

    push @c, \@{$hash{$_}}; same note as before, dereferencing an array only to take the reference to it does not make sense. The main reason why all answers are so different is because of that (as in, people aren't 100% sure why you do that, or atleast that's what I believe)

    Some side notes:

    • It might be easier if you store $a[$x][1] in a temp variable in the scope of the loop... it will increase readibilty
    • The loops can be writen as for my $x (0 .. $#b)
    • Be careful when you use variables called 'a' and 'b' when you use sort... you might confuse other people
    • Because of a lower precedence I find it better to use 'and' instead of '&&' in an if-statement (etc)...

      Oops, you're right about the array references.  It was written the way it was for two good/indifferent/bad reasons:

      • It was coded very late at night.
      • I come from a programming background that stresses being paranoid about references.
      Also, thanks for the ref vs. defined and sidenote suggestions.