Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

shift split grep question

by Xilman (Hermit)
on Nov 25, 2018 at 18:37 UTC ( [id://1226300]=perlquestion: print w/replies, xml ) Need Help??

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

ISTM that the following three lines ought to be a one-liner

@headings = grep /^\s*AUID/, @lines; @headings = split ' ', $headings[0]; shift @headings;

In the above, I know that precisely one element of @lines contains a string of the form "AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments" and I'm trying to get an array which contains ('RA', 'Dec', 'Label', 'V', 'B-V', 'Comments'). I've tried numerous variants on

@headings = shift split ' ', grep /^\s*AUID/, @lines;
including adding parentheses around the grep in an attempt to force list context, but get either grep's result as a scalar 1 or weird and wonderful syntax errors. Can anyone help please?

FWIW, I voted "I am an idiot" in the recent poll "My code is most likely broken because:"

Replies are listed 'Best First'.
Re: shift split grep question (updated)
by haukex (Archbishop) on Nov 25, 2018 at 19:23 UTC

    Your code is doing the following:

    1. Find all the elements of @lines that begin with AUID (plus some optional whitespace) and store them in @headings,
    2. take the first found element and do a split on ' ' (which has a special meaning as explained in its docs) and overwrite the contents of @headings with the result, and
    3. shift the first element off @headings and discard it.

    There are two problems with the oneliner you showed:

    • grep in scalar context (as it is in the code you showed) will return the number of matches, not one of the matches, and
    • shift removes the first element from an array and returns the element it removed, not the list minus the first element.

    <update2> Eily's suggestion is nicest IMO:

    my (undef, @headings) = split ' ', first {/^\s*AUID/} @lines;


    You can pack it in a single line, here is one way, but it's kind of ugly. This uses (...)[0] to return the first return value of grep and splice to return the list without its first element:

    my @headings = splice @{[ split ' ', (grep /^\s*AUID/, @lines)[0] ]}, 1;

    Although I might recommend using first from List::Util to avoid the wasteful grepping of the entire array:

    my @headings = splice @{[ split ' ', first {/^\s*AUID/} @lines ]}, 1;

    I still don't think this is very pretty because of the @{[]} trick I have to use to get splice to be happy. This one, using map and a regex, I like better:

    my @headings = map {/(?!^\s*AUID)\s(\S+)/g} first {/^\s*AUID/} @lines;

    Update: map isn't really needed:

    my @headings = (first {/^\s*AUID/} @lines)=~/(?!^\s*AUID)\s(\S+)/g;

      Rather than splice you can use undef to ignore unwanted elements. Like this: my (undef, @headings) = split ' ', first {/^\s*AUID/} @lines;

      Going further (it's not related to the OP anymore) it works exactly as if there was one scalar in that place that was then ignored. So in the following example: (undef, $second, undef, $hash{fourth}, @rest, undef) = some_function() The first and third values are ignored, the second and fourth get in $second and $hash{fourth}, but @rest gets all the following values, and the last undef is useless. So it let's you ignore values counting from the left, but never couting from the right.

        You're right of course, and that approach is probably much better in this case - thanks for pointing it out!

        (That's what I get for posting hastily on a Sunday evening :-/ )

      haukex has pointed out one problem with your use of shift. If this were the only problem, you could use splice instead. However, neither will work because both require an array (not a list). The difference is subtle, but the documentation of both have it right.
Re: shift split grep question
by tybalt89 (Monsignor) on Nov 25, 2018 at 18:52 UTC
    #!/usr/bin/perl # use strict; use warnings; my @lines = <<END =~ /.*\n/g; other stuff AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments more stuff END my @headings = (grep /^\s*AUID/, @lines)[0] =~ /\t(\S+)/g; use Data::Dump 'dd'; dd \@headings;
Re: shift split grep question
by eyepopslikeamosquito (Archbishop) on Nov 25, 2018 at 20:34 UTC

    To do it without modules try:

    my ( undef, @headings ) = split ' ', (grep /^\s*AUID/, @lines)[0];
    or using the "Baby cart" secret operator (see perlsecret):
    my ( undef, @headings ) = split ' ', shift @{[grep /^\s*AUID/, @lines] +};

Re: shift split grep question
by LanX (Saint) on Nov 25, 2018 at 18:58 UTC
    It's certainly possible but your problems description is fuzzy.

    Please clarify. :)

    use strict; use warnings; use Data::Dump qw/pp dd/; my @lines =<DATA>; my @headings = grep /^\s*AUID/, @lines; @headings = split ' ', $headings[0]; shift @headings; pp @headings; __DATA__ AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments

    ( "\\cIRA", "\\cIDec", "\\cILabel", "\\cIV", "\\cIB-V", "\\cIComments", )

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      "\cI" is "\t" (tab), that's my guess as to what's meant...

Re: shift split grep question
by Xilman (Hermit) on Nov 25, 2018 at 20:29 UTC

    My thanks to those who replied with solutions.

    In response to the comment about the "AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments" imprecision, that was cut&pasted directly from a perl -d session and I'd hoped it would be recognized as such but you're right: I should have been more precise.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1226300]
Approved by haukex
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-04-15 00:41 GMT
Find Nodes?
    Voting Booth?

    No recent polls found