Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

rearranging matched parts of a string while using /g and without using split

by jbullock35 (Hermit)
on Jun 22, 2010 at 17:21 UTC ( [id://845934]=perlquestion: print w/replies, xml ) Need Help??

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

I occasionally have strings like this:

A # B # 1 # 2

The strings have two types of data that I care about. (In this case, letters and numbers.) Each type has the same number of instances within a string. (In this case, there are two letters and two numbers. But sometimes I will have ten of each type.) And all instances of the second type follow all instances of the first type. (In this case, the all of the letters are listed before the numbers begin.)

I want to rearrange the string so that I end up with

A # 1 # B # 2

This is easy to do with split, but is there a way to do it in a single line (presumably without using split)?

Crucially, the number of groups that I want to rearrange will vary. In the example that I've given, there are just four groups, but in other cases there will be eight or ten or more. I'd like a single regular expression that can handle strings that differ on this dimension.

Replies are listed 'Best First'.
Re: rearranging matched parts of a string while using /g and without using split
by ikegami (Patriarch) on Jun 22, 2010 at 18:23 UTC

    One statement:

    $_='A # B # 1 # 2'; print join ' # ', sub { map @_[$_,$_+@_/2], 0..$#_/2 }->( split / # / );

    More readable:

    sub pair { map @_[$_,$_+@_/2], 0..$#_/2 } $_='A # B # 1 # 2'; print join ' # ', pair split / # /;

    I didn't see the purpose of avoiding split — you didn't say what you didn't like about it — but you could replace
    split / # /
    with
    /(?:(?! # ).)*/sg

Re: rearranging matched parts of a string while using /g and without using split
by Yary (Pilgrim) on Jun 22, 2010 at 17:45 UTC
    Is two lines OK?
    $_='A # B # 1 # 2'; my @tokens=/\w+/g; print join ' # ', @tokens[map($_*2,0..$#tokens/2),map(1+$_*2,0..$#tokens/2)];
    (updated, original code only handled tokens that were 1 char long)
Re: rearranging matched parts of a string while using /g and without using split
by kennethk (Abbot) on Jun 22, 2010 at 18:51 UTC
    One line, using a substitution wrapped in a while loop and no split. Obviously, you can remove the print statements with no ill effect. A split-base solution as offered by Yary and ikegami is probably clearer and more maintainable. This algorithm assumes you can easily differentiate your keys from your values, as in your example.
    #!/usr/bin/perl use strict; use warnings; my $n_terms = 5; my $string = ""; my $letter = "A"; $string .= $letter++ . " # " for (1 .. $n_terms); $string .= join " # ", 1 .. $n_terms; print "Input: $string\n"; while ($string =~ s/((?:^|[0-9]+\s*\#\s*)[A-Z]+\s*\#\s*) ([^0-9]+?\#\s*) ([0-9]+\s*\#\s*) /$1$3$2/x) { print "Intermediate: $string\n"; }; print "Output: $string\n"; __END__ Input: A # B # C # D # E # 1 # 2 # 3 # 4 # 5 Intermediate: A # 1 # B # C # D # E # 2 # 3 # 4 # 5 Intermediate: A # 1 # B # 2 # C # D # E # 3 # 4 # 5 Intermediate: A # 1 # B # 2 # C # 3 # D # E # 4 # 5 Intermediate: A # 1 # B # 2 # C # 3 # D # 4 # E # 5 Output: A # 1 # B # 2 # C # 3 # D # 4 # E # 5

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2024-04-23 12:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found