in reply to shift split grep question

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:

<update2> Eily's suggestion is nicest IMO:

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

</update2>

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;
use warnings; use strict; use Test::More tests=>6; use List::Util qw/first/; my @lines = ("AUID \cIRA \cIDec \cILabel \cIV \cIB-V \cIComments", 'hello','world'); my @expect = ('RA', 'Dec', 'Label', 'V', 'B-V', 'Comments'); { my @headings = grep /^\s*AUID/, @lines; @headings = split ' ', $headings[0]; shift @headings; is_deeply \@headings, \@expect or diag explain \@headings; } { my @headings = splice @{[ split ' ', (grep /^\s*AUID/, @lines)[0] ]}, 1; is_deeply \@headings, \@expect or diag explain \@headings; } { my @headings = splice @{[ split ' ', first {/^\s*AUID/} @lines ]}, 1; is_deeply \@headings, \@expect or diag explain \@headings; } { my @headings = map {/(?!^\s*AUID)\s(\S+)/g} first {/^\s*AUID/} @lines; is_deeply \@headings, \@expect or diag explain \@headings; } { my @headings = (first {/^\s*AUID/} @lines)=~/(?!^\s*AUID)\s(\S+)/g +; is_deeply \@headings, \@expect or diag explain \@headings; } { my (undef, @headings) = split ' ', first {/^\s*AUID/} @lines; is_deeply \@headings, \@expect or diag explain \@headings; }

Replies are listed 'Best First'.
Re^2: shift split grep question
by Eily (Monsignor) on Nov 25, 2018 at 19:44 UTC

    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 :-/ )

Re^2: shift split grep question
by BillKSmith (Monsignor) on Nov 25, 2018 at 21:59 UTC
    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.
    Bill