Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
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 ( #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.

Comment on rearranging matched parts of a string while using /g and without using split
Select or Download Code
Re: rearranging matched parts of a string while using /g and without using split
by Yary (Scribe) 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 ikegami (Pope) 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 kennethk (Monsignor) 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
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? | Other CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2014-11-24 09:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My preferred Perl binaries come from:














    Results (137 votes), past polls