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

This works:
my $usefulans; foreach ( @$qablock ) { if ( $$_[ 0 ] eq $answer ) { $usefulans = [ @$_ ]; } } print "\t\t----> @$usefulans\n";
...but it is terribly inelegant. I would like to replace it with a simple grep:
my $usefulans = grep { $$_[ 0 ] eq $answer } @$qablock; print "\t\t----> @$usefulans\n";
...but nothing (and I mean NOTHING...I've tried every permutation I could imagine, even throwing a map in for good measure) I do makes it work. No matter how I try to dereference the array of arrays, I end up with the reference address (e.g. "ARRAY(0x7fed6283b5d0)") instead of the legitimate list of data that I get from the foreach above. Am I stuck with the foreach, because this is something that grep just can't handle? Or is there something I'm doing wrong, which, done correctly, would make the grep work?

Thanks in advance,

-Brian

Replies are listed 'Best First'.
Re: grep and dereferencing an array of arrays
by McA (Priest) on Sep 06, 2013 at 18:48 UTC

    Hi,

    you have an error here:

    my $usefulans = grep { $$_[ 0 ] eq $answer } @$qablock;

    With the assigmnent to $usefulans you force scalar context. That means you assign the length of the resulting grep operation to this variable. Afterwards you try to derefence it. I'm pretty sure that with strict and warnings you would have got the right hints.

    Update: my $usefulans = [ grep { $$_[ 0 ] eq $answer } @$qablock ]; should solve your problem.

    Best regards
    McA

      You're right about the mistake, except that what grep returns is already a hashref so by adding [ ] around it, you make an array of array

      You can just provide a list context to the expression with ($usefulans,) (the comma is optional, but I put it there to show that the parenthesis is there to force list context), which will put the first result into $usefulans and drop the others. But since there should be only one match ...

      $VAR1 = [ [ 'a', 'W', 'interupt' ], [ 'b', 'R', 'interrupt' ], [ 'c', + + + 'W', + + + 'innterupt' + + + ], + + + [ + + + 'd', + + + 'W', + + + 'intterupt' + + + ] + + + ]; + + + ($answer,) = grep { $$_[0] eq 'b' } @$VAR1; + + + print "@$answer";
      b R interrupt

      But oakb, you should consider a hash (with answer id as the key). You could easily turn your array into one with : %hash = map { shift @$_ => $_ } @$qablock Then you'd just have to do $hash{$givenAnswer} to get ['w', 'interupt']

        A ++ for your hash advice.

        McA

        Using a hash is a great idea, one that I contemplated earlier but discarded because other parts of the program rely on the data being maintained in an ordered list -- which arrays provide but hashes do not.

        Honestly, I'm still trying to get my head around why the grep returns its data in a list context while the foreach does not. I expected the two routines to be equivalent....
      I thought the same thing. When I said that I've tried every permutation that I can imagine, my attempts included many variations on this:
      my $usefulans = [ grep { $$_[ 0 ] eq $answer } @$qablock ];
      ...which should make the assignment as an anonymous reference. But none of those worked any better than the bracket-less assignment. I still end up with the reference address.

      Update: you updated while I was replying. You probably have already seen me saying that adding brackets doesn't work.

        Can you show me Dumper($qablock);

        Update: I don't know what you want to achieve with the first code block. Do you really just want the last matching result?

        McA

Re: grep and dereferencing an array of arrays
by BillKSmith (Monsignor) on Sep 07, 2013 at 14:41 UTC
    use strict; use warnings; my $qablock = [ [qw(r0c0 r0c1 r0c2)], [qw(r1c0 r1c1 r1c2)], [qw(r2c0 r2c1 r2c2)], ]; my $answer = 'r1c0'; my ($usefulans) = grep {$$_[0] eq $answer} @$qablock; print "\t\t----> @$usefulans\n";
    Output:
    ----> r1c0 r1c1 r1c2
    Bill