Re: need help to split list
by BrowserUk (Patriarch) on Oct 24, 2003 at 21:54 UTC
|
@a=map{m[(\d+)([a-z]?)-(\d+|[a-z])]and$2?(map{$1.$_}$2..$3):($1..$3)}s
+plit',',$s;
print "@a";
1 2 3 4 5 6a 6b 6c 6d 6e 10 11 12 13
Update: The above completely misses the 7!
Corrected and expanded for a little more clarity
print map{
m[ (\d+) ([a-z]?) -? (\d+|[a-z])? ]x
and $2
? ( map{ $1 . $_ } $2 .. $3 )
: $3 ? ( $1 .. $3 )
: ( $1 )
}split ',', $s;
1 2 3 4 5 6a 6b 6c 6d 6e 7 10 11 12 13
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!
| [reply] [d/l] [select] |
|
|
#!/usr/bin/perl -l
my $s = '1-5,6a-e,7,10-13';
my @a = map{
if (m[ (\d+) ([a-z]?) - (\d+|[a-z]) ]x) {
if ($2) {
map{ $1 . $_ } $2 .. $3;
} else {
( $1 .. $3 );
}
} else {
$_;
}
} split ',', $s;
print join ' ', @a;
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
Re: need help to split list
by gjb (Vicar) on Oct 24, 2003 at 22:02 UTC
|
A bit longer than BrowserUK's approach, but probably easier to understand and a bit more robust:
#!/usr/bin/perl
use strict;
my $list = '1-5,6a-e,7,10-13';
my @list = split(/\s*,\s*/, $list);
print join("\n", expand($_)), "\n" foreach @list;
sub expand {
my $str = shift;
if ($str =~ /^(\d+)\-(\d+)$/) {
return ($1..$2);
} elsif ($str =~ /^(\d+)([a-z]+)\-([a-z]+)$/) {
return map {$1.$_} ($2..$3);
} elsif ($str =~ /^\d+$/) {
return $str;
} else {f
die("wrong format");
}
}
Note that it doesn't check whether the ranges are valid, e.g. whether $1 <= $2, but that's trivial to add.
Hope this helps, -gjb- | [reply] [d/l] [select] |
|
|
Quite good and very readable! Thanks.
| [reply] |
Re: need help to split list
by chromatic (Archbishop) on Oct 24, 2003 at 22:00 UTC
|
This was a fun one. Here's a non-golfy solution I quite like, despite its assumptions.
#!/usr/bin/perl -w
use strict;
use Test::More tests => 1;
my @list = qw( 1 2 3 4 5 6a 6b 6c 6d 6e 7 10 11 12 13 );
chomp( my $range = <DATA> );
my @result = split_list( $range );
is_deeply( \@result, \@list );
sub split_list
{
my $range = shift;
my @list;
for my $r ( split( /,/, $range ) )
{
my $iter = get_points( $r );
while (my $next = $iter->())
{
push @list, $next;
}
}
return @list;
}
sub get_points
{
my $range = shift;
my ($start, $end) = split( /\-/, $range );
return iter_int($start, $end) unless $start =~ /\D/;
my ($number, $letter) = $start =~ /^(\d+)(\D)$/;
return iter_str( $number, $letter, $number . $end );
}
sub iter_int
{
my ($start, $end) = @_;
$end ||= $start;
return sub
{
return if $start > $end;
return $start++;
};
}
sub iter_str
{
my ($start, $letter, $end) = @_;
my $last = '';
return sub
{
return if $last eq $end;
my $next = $start . $letter;
$last = $start . $letter++;
return $next;
};
}
__DATA__
1-5,6a-e,7,10-13
| [reply] [d/l] |
Re: need help to split list
by Cody Pendant (Prior) on Oct 24, 2003 at 21:58 UTC
|
This takes care of your hyphenated numbers:
$s = '1-5,6a-e,7,10-13';
@s = split ( ',', $s );
for (@s) {
if (/(\d+)-(\d+)/) {
$tmp = '';
for ( $1 .. $2 ) {
$tmp .= "$_ ";
}
$_ = $tmp;
}
}
print "@s";
# 1 2 3 4 5 6a-e 7 10 11 12 13
($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
=~y~b-v~a-z~s; print
| [reply] [d/l] [select] |
Re: need help to split list
by tilly (Archbishop) on Oct 25, 2003 at 05:48 UTC
|
TIMTOWTDI. But some ways are nastier than others. ;-)
$s='1-5,6a-e,7,10-13';
$s =~ s/,/ /g;
$s =~ s/(\d+)-(\d+)/"{".(join ",", $1..$2)."}"/ge;
$s =~ s/([a-z]+)-([a-z]+)/"{".(join ",", $1..$2)."}"/ge;
$s =~ s/([A-Z]+)-([A-Z]+)/"{".(join ",", $1..$2)."}"/ge;
print "$_\n" for glob($s);
| [reply] [d/l] |
Re: need help to split list
by daddyefsacks (Pilgrim) on Oct 25, 2003 at 00:13 UTC
|
Well when I started there were no answers but oh well, I'll post my unworthy code anyway.
set_ranged_data("1-5,6a-e,7w-z,10-13");
while(my @enumerated = get_next_range()) {
print join("\n", @enumerated);
print "\n";
}
{
my $data;
my $pos;
sub set_ranged_data {
$data = shift;
$pos = 0;
1;
}
sub get_next_range {
return unless $data;
my $end_pos = index($data, ",");
$end_pos = length($data) if ($end_pos) < 0;
my $range = substr($data, 0, $end_pos);
substr($data, 0, $end_pos + 1) = "";
my @enumerated = _enumerate($range);
wantarray ? return @enumerated
: return \@enumerated
}
sub _enumerate {
my $range = shift;
return ($range) unless index($range, "-") > 0;
my ($set, $end_point) = split /-/, $range;
my ($begin_lead, $begin_point) = _decompose($set);
return
map {$begin_lead . $_} ($begin_point .. $end_point);
}
sub _decompose {
my $set = shift;
my ($lead, $point) = $set =~ /^(\d+)?(\D)?$/;
return ($lead, $point) if ($lead && $point);
return ("", $point) if ($point);
return ("", $lead) if ($lead);
return;
}
}
| [reply] [d/l] |
|
|
Thanks for the code even though there were already answers. This is what the site is all about and the many different ways of doing this helps me to expand my knowledge of the language.
Thanks,
Ed
| [reply] |
Re: need help to split list
by Roger (Parson) on Oct 25, 2003 at 12:04 UTC
|
Another method, one liner using two splits:
use strict;
use Data::Dumper;
my $list = "1-5,6a-e,7,9-12,89xDC-G";
my @s = map { my @t = split /-/;
my $l = length($t[0]) - 1; # length less 1
$#t == 0 ? $_ :
$t[0] + 0 eq $t[0] ? # pure number?
$t[0] .. $t[1] :
map { substr($t[0],0,$l) . $_ }
substr($t[0],$l,1) .. $t[1]
} split /,/, $list;
print Dumper(\@s);
And the output is:
$VAR1 = [
1,
2,
3,
4,
5,
'6a',
'6b',
'6c',
'6d',
'6e',
'7',
9,
10,
11,
12,
'89xDC',
'89xDD',
'89xDE',
'89xDF',
'89xDG'
];
Update: Typo fixed - 89xDC-G (was 89xFC-G). Thanks OverlordQ!
| [reply] [d/l] [select] |
|
|
Edit: edit of course it's typo *grin*
my $list = "1-5,6a-e,7,9-12,89xFC-G";
and then
'89xDC',
'89xDD',
'89xDE',
'89xDF',
'89xDG'
Did I miss something, or was it a typo? (89xFC changed to 89xDC) | [reply] |
|
|
Thanks for catching my typo! Yes I ran the code with 89xDC-G, and when I posted the code, I pasted first, and then decided to try some new data, and instead of cut and paste the modified code again, just editted by hand...
| [reply] |
Re: need help to split list
by qq (Hermit) on Oct 26, 2003 at 22:19 UTC
|
Similar to gjb 's, I see:
use warnings;
use strict;
my $str = '1-5,6a-e,7,10-13';
my @ranged;
foreach ( split ',', $str ) {
if ( /^(\d+)$/ ) {
push @ranged, $1;
} elsif ( /^(\d+)([a-z]+)-([a-z]+)$/ ) {
push @ranged, map { "$1$_" } $2..$3;
} elsif ( /^(\d+)-(\d+)$/ ) {
push @ranged, $1..$2;
} else {
die "Don't know how to interpret '$_'";
}
}
print join "\n", @ranged;
| [reply] [d/l] |