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

Due to my own programming vocabulary deficiencies, poor Google foo, or both I'm unable to search effectively for an answer to this syntax question. Sparing what I hope is unnecessary detail, I have in the past done the following and was amazed (not for the first time) that Perl let me do this and furthermore did exactly what I wanted.

my $sales_line = join "\t", @data[@sales_indexes{@sales_columns}];

Where I use the @sales_columns array to give me a hash slice of %sales_indexes (using @ instead of % for a list) which I use to take an array slice of @data, join it with tabs, and then store the result as the $sales_line string. Yeah... I think I said that all correctly. When this worked it was one of those, "damn I love Perl" moments for me. I often physically walk over to the only other Perl user I know and say exactly that whenever they happen.

Which brings me to my question, because it's somewhat similar, and it's a rare case of Perl not understanding what I meant and/or doing what I wanted, and I'm hoping the error is mine and I just have the exact syntax wrong. I tried the following which gives a syntax error.

my $transcripts_line = join "\t", @transcripts{$transcript_ip}{$transcript_id}{@transcripts_columns};

Where %transcripts is a 3D hash that I'm trying to use the same up front @ instead of % trick to get the value list I want from the hash slice that I was hoping would be provided by the @transcripts_columns array ($transcript_ip and $transcript_id are simple scalars). I attempted the below as well, which doesn't give a syntax error, but doesn't work as expected either.

my $transcripts_line = join "\t", ($transcripts{$transcript_ip}{$transcript_id}{@transcripts_columns});

The way I did solve my problem was as follows.

my $transcripts_line = join "\t", map {$transcripts{$transcript_ip}{$transcript_id}{$_}} @transcripts_columns;

Which is fine, but... the first line of code I gave that worked was just so, well... succinct and pretty. :-)

As I said, I'm hoping I've just messed up the syntax with what I originally tried and I don't actually have to resort to the map inside of a join to make this work.

Any thoughts from the monastery?

I love it when things get difficult; after all, difficult pays the mortgage. - Dr. Keith Whites
I hate it when things get difficult, so I'll just sell my house and rent cheap instead. - perldigious

Replies are listed 'Best First'.
Re: Syntax Question Related to "join" on a Complex Data Structure
by choroba (Cardinal) on Oct 25, 2016 at 21:19 UTC
    Just tell Perl what to dereference:
    my $transcripts_line = join "\t", @{ $transcripts{$transcript_ip}{$tra +nscript_id} }{@transcripts_columns};

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

      That worked. I was so close... For dereferencing I had tried (and it obviously failed).

      my $transcripts_line = join "\t", @{ $transcripts{$transcript_ip}{$tra +nscript_id}{@transcripts_columns} };

      So the location of that dereference closing bracket made all the difference. I need to stare at this (and look deeper with print) for a minute or two until it makes sense to me why I should be dereferencing a list from just the first two keys before taking a hash slice of that list with the @transcripts_columns array... That's what it looks like your code is doing to me at first glance anyway, perhaps I'm even interpreting that wrong.

      Thanks choroba. I'll upvote tomorrow, I'm out of votes for the day.

      I love it when things get difficult; after all, difficult pays the mortgage. - Dr. Keith Whites
      I hate it when things get difficult, so I'll just sell my house and rent cheap instead. - perldigious

      Well, I did look deeper, and I mostly understand what's going on now, but one thing does still surprise me.

      say $transcripts{$transcript_ip}{$transcript_id}; + # gives me the hash reference of the lowest (3rd deep + hash) say %{ $transcripts{$transcript_ip}{$transcript_id} }; + # gives me that entire hash (key value pairs) say %{ $transcripts{$transcript_ip}{$transcript_id} }{@transcri +pts_columns}; # gives me my desired slice of that hash (key value p +airs) say keys { %{ $transcripts{$transcript_ip}{$transcript_id} }{@transcri +pts_columns} }; # gives me my desired slice of that hash (keys only) say @{ $transcripts{$transcript_ip}{$transcript_id} }{@transcri +pts_columns}; # gives me my desired slice of that hash (values only +)

      What slightly surprised me is that the 3rd line above (with the leading %) gives the key value pairs and not just the values, but that made sense when I considered that I did essentially ask it to give me a hash and of course Perl would give a list of key value pairs when I do that. What still surprises me is that the last line simply by changing the leading % to a @ gives just the values to the list. Don't get me wrong though, that's darned convenient, and I'm really glad Perl does that.

      I didn't even realize that's the same thing I had done before in the first line of code I gave in my original post until I went back and considered it more. For some reason that made complete sense to me at the time... or more likely I guessed, got lucky, and didn't inspect further because I didn't have to at the time. :-)

      I love it when things get difficult; after all, difficult pays the mortgage. - Dr. Keith Whites
      I hate it when things get difficult, so I'll just sell my house and rent cheap instead. - perldigious

        Hi perldigious,

        What slightly surprised me is that the 3rd line above (with the leading %) gives the key value pairs and not just the values

        Key/Value Hash Slices were added in Perl v5.20 (the rest of the Slices doc is probably worth a read too).

        Hope this helps,
        -- Hauke D

Re: Syntax Question Related to "join" on a Complex Data Structure
by tybalt89 (Monsignor) on Oct 25, 2016 at 21:57 UTC

    try:

    my $transcripts_line = join "\t", $transcripts{$transcript_ip}{$transc +ript_id}->@{@transcripts_columns};

    if you have a sufficiently modern perl.

      I do use 5.022 so I have a sufficiently modern version of Perl, though I admit I prefer the old notation choroba used, probably because I'm not very Object Oriented minded (though I admit yours is easier to read at a glance tybalt89). That said, I tried it, and it worked exactly the same, but it gave me the following warning along the way (not sure why).

      Array found where operator expected at Web_Chat_IP_Match.pl line 141, +near "->@" (Missing operator before @?)

      I've noticed that different people prefer different ways when doing things like this: &{}, %{}, @{}, ${} vs. &$, %$, @$, $$ vs. ->{} and ->[].

      I've sensed rumblings of a past holy war, or at least a past split in the clergy that lead to the Perl programmer equivalents of Lutheranism or Protestantism in some of the things I've read about it.

      I love it when things get difficult; after all, difficult pays the mortgage. - Dr. Keith Whites
      I hate it when things get difficult, so I'll just sell my house and rent cheap instead. - perldigious

        G'day perldigious,

        This is to address a number of points in this branch of the thread; in particular, versions, features, warnings and interpolating postfix dereferencing within strings.

        Firstly, starting with v5.24:

        $ perl -v | head -2 | tail -1 This is perl 5, version 24, subversion 0 (v5.24.0) built for darwin-th +read-multi-2level

        This code recognises postderef syntax outside of strings but not within strings:

        #!/usr/bin/env perl -l use strict; use warnings; my $x = [qw{test string}]; print $x->@*; print "$x->@*";

        Output:

        teststring ARRAY(0x7fc63c005498)->@*

        Adding either of these:

        use 5.024;

        or

        use feature qw{postderef_qq};

        Allows interpolation within strings:

        Output:

        teststring test string

        Now looking at v5.20.

        $ perl -v | head -2 | tail -1 This is perl 5, version 20, subversion 0 (v5.20.0) built for darwin-th +read-multi-2level

        Output using the same initial code as before:

        Array found where operator expected at ./pm_1174734_postderef.pl line +11, at end of line (Missing operator before ?) syntax error at ./pm_1174734_postderef.pl line 11, near "->@*" Execution of ./pm_1174734_postderef.pl aborted due to compilation erro +rs.

        To make this work, you need to turn off the experimental warnings (after the use warnings line) and use both the postderef and the postderef_qq features.

        use warnings; no warnings qw{experimental::postderef}; use feature qw{postderef postderef_qq};

        Using the code and version shown, this outputs:

        teststring test string

        Specifying a minimum version in the code, but running with a lesser version, gives:

        Perl v5.20.0 required--this is only v5.18.0, stopped at ... BEGIN failed--compilation aborted at ...

        This tells you how to fix but not why.

        Not specifying the version, gives:

        Unknown warnings category 'experimental::postderef' at ... BEGIN failed--compilation aborted at ...

        This provides the why but not the how (i.e. you'll need some research to determine if that's a valid category and, if so, at what version it was introduced).

        I prefer to specify the version. You may have a different preference or, perhaps, might be guided by in-house coding standards.

        See also:

        Updates:

        • change of meaning: s{expected to be a dead link}{will refer to a different delta}
        • additional doco: Added manpage names to links: perldelta & perl52400delta

        — Ken