Miguel has asked for the wisdom of the Perl Monks concerning the following question:
Esteemed monks,
I've been looking for a way to convert some parts of a string into an array and all the string into an hash table.
My best effort is shown here:
#!/usr/bin/perl -w
use strict;
my $string = "1:1,2:1,3:2,500:2,505:1";
my @array_1 = split ",",$string;
my (@array_2, @array_temp, %hash_1);
foreach ( @array_1 ) {
@array_temp = split ":",$_;
push @array_2, $array_temp[0];
$hash_1{$array_temp[0]} = $array_temp[1];
}
At the end I'll have:
@array_2 = (1,2,3,500,505)
%hash_1 = (
1 => 1,
2 => 1,
3 => 2,
500 => 2,
505 => 1,
)
Now, my question is: is there a better and more efficient way to have this done? perhaps with some complex regular expressions?
Thanks, Miguel
Re: better way to convert a string into an array and an hash
by dragonchild (Archbishop) on Oct 05, 2004 at 14:48 UTC
|
Looks good to me. I'd write it like this:
my $string = " ... ";
my (@array_2, %hash_1);
foreach ( split ',' => $string ) {
my ($left, $right) = split ':';
push @array_2, $left;
$hash_1{$left} = $right;
}
You don't need @array_1 at all. You should scope @array_temp more tightly. And, in fact, I would label the actual items of @array_temp. Otherwise, it looked fine.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] [d/l] |
Re: better way to convert a string into an array and an hash
by duff (Parson) on Oct 05, 2004 at 14:53 UTC
|
I don't know if it's better, but you can do this:
my $string = "1:1,2:1,3:2,500:2,505:1";
my %hash = split /[:,]/, $string;
my @array = keys %hash;
Whether it's better depends on how much you know and can guarantee about your data. :-)
| [reply] [d/l] |
|
FYI: that's not functionally equivalent. You lose the ordering in @array.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] |
|
my $string = "1:1,2:1,3:2,500:2,505:1";
my %hash = split /[:,]/, $string;
my @array = split /:.+?,?/, $string; # :-)
or another:
my $string = "1:1,2:1,3:2,500:2,505:1";
my %hash = my @array = split /[:,]/, $string;
@array = @array[grep $_%2==0, 0..$#array];
But these probably drive the original split idea far far into the ground :-)
| [reply] [d/l] [select] |
Re: better way to convert a string into an array and an hash
by davorg (Chancellor) on Oct 05, 2004 at 15:24 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
$_ = '1:1,2:1,3:2,500:2,505:1';
my (@array, %hash);
%hash = map { my @x = split /:/; push @array, $x[0]; @x } split /,/;
use Data::Dumper;
print Dumper \@array, \%hash;
--
< http://www.dave.org.uk>
"The first rule of Perl club is you do not talk about
Perl club." -- Chip Salzenberg
| [reply] [d/l] |
|
my (@a, %h);
$_ = '1:1,2:1,3:2,500:2,505:1';
#234567890#234567890#234567890#234567890#234567890#234567890
# 47 characters ...
%h=map{@@=/[^:]+/g;$a[$#a+1]=$@[0];@@}/[^,]+/g;
# 44 ...
%h=map{@@=/[^:]+/g;push@a,$@[0];@@}/[^,]+/g;
# Based on duff's whimsy above ... 42 characters
%h=@a=/[^:,]+/g;@a=@a[grep!($_%2),0..$#a];
# Further whimsy ... 29 characters
%h=/[^:,]+/g;@a=/([^,:]+):/g;
Rules:
- You can assume all the code above the counting line.
- Bonus points if you run under strict and warnings.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] [d/l] |
|
If you convert my answer to use short variable names and remove the ?: you get 44 relevant chars
#234567890#234567890#234567890#234567890#234567890#234567890
s/(\d+):(\d+)(,|$)/$h{$1}=$2;push(@a,$1)/ge;
| [reply] [d/l] |
|
| [reply] [d/l] |
|
I'm not a golfer, but...
tr/:/,/;%h=eval;@a=keys%h;
Update
y/:/,/;%h=eval;@a=keys%h;
Update 2
If the ordering of @a is important, how about
@a=/(\d+):/g;y/:/,/;%h=eval;
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: better way to convert a string into an array and an hash
by fergal (Chaplain) on Oct 05, 2004 at 14:56 UTC
|
use strict;
use warnings;
my $string = "1:1,2:1,3:2,500:2,505:1";
my (@array, %hash);
$string =~ s/(\d+):(\d+)(?:,|$)/$hash{$1}=$2;push(@array, $1)/ge;
use Data::Dumper;
print Dumper(\@array, \%hash);
the e on the end of the s// tells perl to execute what's on the right hand side and the g tells it to match the left hand side as many times as possible. So you effectively end up with the code on the right in a loop over all the things that match the pattern.
The (?:,|$) makes sure that the pair of number si followed by a , or that we're at the end of the string.
One thing to note is that this will destroy the contents of $string. | [reply] [d/l] |
|
while ($string =~ /(\d+):(\d+)(?:,|$)/g) {
$hash{$1} = $2;
push(@array, $1);
}
over
$string =~ s/(\d+):(\d+)(?:,|$)/$hash{$1}=$2;push(@array, $1)/ge;
| [reply] [d/l] [select] |
|
use warnings; use strict;
my $string = "1:1,2:1,3:2,500:2,505:1";
my (@array, %hash);
while ($string =~ /(\d+):(\d+)(?:,|$)/g)
{ $hash{$1}=$2;push(@array, $1) };
use Data::Dumper;
print Dumper(\@array, \%hash);
| [reply] [d/l] [select] |
|
$p =~ s/(.*?)=(.*?)(&|$)/$v=$2;$v=~s/%(..)/chr(hex($1))/ge;$p{$1}=$v/g
+e
a substitution within a substiution!
I also thought it might be quicker but after a quick benchmark it seems not.
I suppose I could have used
[/(\d+):(\d+)(?:,|$)(?{$hash{$1}=$2;push(@array, $1);1})/g];
for a free loop without destroying the string but isn't much faster. | [reply] [d/l] [select] |
Re: better way to convert a string into an array and an hash
by ambrus (Abbot) on Oct 05, 2004 at 17:14 UTC
|
My guess is:
my $string = "1:1,2:1,3:2,500:2,505:1";
my %hash = $string=~/([-.\w]*):([-.\w]*),/g;
my @array = $string=~/([-.\w]*):/g;
use Data::Dumper; print Dumper \@array, \%hash;
Update: that's wrong of course. Here's the corrected one:
my $string = "1:1,2:1,3:2,500:2,505:1";
my %hash = $string=~/([-.\w]*):([-.\w]*)/g;
@array = $string=~/([-.\w]*):/g;
use Data::Dumper; print Dumper \@array, \%hash;
Sorry. | [reply] [d/l] [select] |
|
|