Choosing grouping of split items

F

fishfry

If I have

$text = 'abc - def - ghi - jkl";

then

@tokens = split(/-/, $text, 3);

splits the line into abc, def, ghi - jkl (ignoring leading and trailing
spaces).

I have an application where there may be 3 or 4 items separated by '-'
but if there are 4, I want them returned as abc - def, ghi, and jkl.

Is there a one-liner for that?
 
J

Juha Laiho

fishfry said:
If I have

$text = 'abc - def - ghi - jkl";

then

@tokens = split(/-/, $text, 3);

splits the line into abc, def, ghi - jkl (ignoring leading and trailing
spaces).

I have an application where there may be 3 or 4 items separated by '-'
but if there are 4, I want them returned as abc - def, ghi, and jkl.

Is there a one-liner for that?

I don't understand the magic of one-lining "everything"; write code as
it fits nicely. Of course, Perl golf is a different issue (writing code
to accomplish a given task with a minimal amount of characters).

As to how I'd solve your problem would be split the data, then join
the first two fields if the split resulted in 4 fields. Alternative
would be to write a regular expression that always returns 3 fields;
something like
/(.*)\s+-\s+([^-]*)\s+-\s+(.*)/
might be close to your needs. Whether this is faster or slower than
splitting and recombining I don't know -- but that you can find out
with the benchmarking modules.
 
A

Anno Siegel

fishfry said:
If I have

$text = 'abc - def - ghi - jkl";

then

@tokens = split(/-/, $text, 3);

splits the line into abc, def, ghi - jkl (ignoring leading and trailing
spaces).

I have an application where there may be 3 or 4 items separated by '-'
but if there are 4, I want them returned as abc - def, ghi, and jkl.

Is there a one-liner for that?

You can force almost everything into a single statement, the question is
if you want to. Here is one way:

my @tokens = reverse map scalar reverse, split /-/, reverse( $str), 3;

How long does it take to find out what this does? And how long for a
plain two-statement procedure:

my @tokens = split /-/, $str;
@tokens = ( "$tokens[ 0]-$tokens[ 1]", @tokens[ 2 .. $#tokens]) if
@tokens > 3;

Anno
 
A

Anno Siegel

fishfry said:
If I have

$text = 'abc - def - ghi - jkl";

then

@tokens = split(/-/, $text, 3);

splits the line into abc, def, ghi - jkl (ignoring leading and trailing
spaces).

I have an application where there may be 3 or 4 items separated by '-'
but if there are 4, I want them returned as abc - def, ghi, and jkl.

Is there a one-liner for that?

You can force almost everything into a single statement, the question is
if you want to. Here is one way:

my @tokens = reverse map scalar reverse, split /-/, reverse( $str), 3;

How long does it take to find out what this does? And how long for a
plain two-statement procedure?

my @tokens = split /-/, $str;
@tokens = ( "$tokens[ 0]-$tokens[ 1]", @tokens[ 2 .. $#tokens]) if
@tokens > 3;

Anno
 
J

Jay Tilton

: If I have
:
: $text = 'abc - def - ghi - jkl";
:
: then
:
: @tokens = split(/-/, $text, 3);
:
: splits the line into abc, def, ghi - jkl (ignoring leading and trailing
: spaces).
:
: I have an application where there may be 3 or 4 items separated by '-'
: but if there are 4, I want them returned as abc - def, ghi, and jkl.

What did you try?

Split the string and rejoin whatever fields you want to be together.

my @tokens = split /-/, $text;
unshift @tokens, join '-', splice @tokens,0,-2;

: Is there a one-liner for that?

If that's what really turns you on.

my @tokens = map scalar reverse, reverse split(/-/, reverse($text), 3);

Yuck. Stick with the two-liner.

Odd. I don't see anywhere in the documentation that says scalar reverse()
defaults to acting on $_ when no argument is supplied, but it evidently
does.
 
U

Uri Guttman

AS> my @tokens = split /-/, $str;
AS> @tokens = ( "$tokens[ 0]-$tokens[ 1]", @tokens[ 2 .. $#tokens]) if
AS> @tokens > 3;

blech!

$tokens[0] .= '-' . splice( @tokens, 1, 1 ) if @tokens > 3;

or

splice( @tokens, 0, 2 ) = join '-', splice( @tokens, 1, 1 )
if @tokens > 3;

uri
 
A

Anno Siegel

Uri Guttman said:
AS> my @tokens = split /-/, $str;
AS> @tokens = ( "$tokens[ 0]-$tokens[ 1]", @tokens[ 2 .. $#tokens]) if
AS> @tokens > 3;

blech!
Agreed.

$tokens[0] .= '-' . splice( @tokens, 1, 1 ) if @tokens > 3;

Much better.
or

splice( @tokens, 0, 2 ) = join '-', splice( @tokens, 1, 1 )
if @tokens > 3;

That's splice( @tokens, ...) twice in a single statement.

Anno
 
U

Uri Guttman

AS> my @tokens = split /-/, $str;
AS> @tokens = ( "$tokens[ 0]-$tokens[ 1]", @tokens[ 2 .. $#tokens]) if
AS> @tokens > 3;
AS> Agreed.
$tokens[0] .= '-' . splice( @tokens, 1, 1 ) if @tokens > 3;

AS> Much better.

AS> That's splice( @tokens, ...) twice in a single statement.

true. i thought it was kinda cute but not optimal. how about this
variant:

$tokens[0] = join '-', shift @tokens, splice( @tokens, 1, 1 ) ;

again it has @tokens twice. :(

oh well. i have other fish to fry.

uri
 
J

John W. Krahn

fishfry said:
If I have

$text = 'abc - def - ghi - jkl";

then

@tokens = split(/-/, $text, 3);

splits the line into abc, def, ghi - jkl (ignoring leading and trailing
spaces).

I have an application where there may be 3 or 4 items separated by '-'
but if there are 4, I want them returned as abc - def, ghi, and jkl.

Is there a one-liner for that?

$ perl -le'
$text = "abc - def - ghi - jkl";
@tokens = $text =~ /(.+)-(.+)-(.+)/;
print for @tokens;
'
abc - def
ghi
jkl



John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top