Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

better way to convert a string into an array and an hash

by Miguel (Friar)
on Oct 05, 2004 at 14:44 UTC ( [id://396603]=perlquestion: print w/replies, xml ) Need Help??

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

Replies are listed 'Best First'.
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.

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. :-)

      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.

        Indeed.

        There are a number of ways to preserve the order. Here's a slightly whimsical one:

        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 :-)
Re: better way to convert a string into an array and an hash
by davorg (Chancellor) on Oct 05, 2004 at 15:24 UTC

    Slightly contrived single line solution:

    #!/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

      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.

        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;

        Surely, %h=/\d+/g;@a=/(\d+):/g; would be closer to what's wanted :)

        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;

        GEEES!!!

        Thank you all for showing me that I still have a lot to learn! :D

        Miguel

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.

      May I recommend

      while ($string =~ /(\d+):(\d+)(?:,|$)/g) { $hash{$1} = $2; push(@array, $1); }

      over

      $string =~ s/(\d+):(\d+)(?:,|$)/$hash{$1}=$2;push(@array, $1)/ge;

      You are using s///ge only for the side effects of the code in the substitution part. Why do you need a substitution then? You could just as well say

      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);

        Simply because it gives you a free loop. I first saw this idiom about 10 years ago for decoding a cgi params into a hash. Something like

        $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.
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.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2024-04-19 15:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found