Parsing a string into a hash

I

IanW

Hi

I have some strings like this (the print_r output from PHP):

my $str = "Array
(
[SY] => 1
[DN] => 1
[YO] => 1
[MK] => 1
[N] => 9
[HP] => 2
[LA] => 1
[NP] => 1
[SW] => 3
)";

I want to be able to parse that string into a Perl hash where the keys are
the letters inside the square brackets (eg: SY or N) and the values are the
numbers after the => (they will always be numeric).

I feel this should be achievable with a cunning one liner using the map
function along with some regexp output (/\[(\w+)\]\D+?(\d+)/) but I don't
know how to begin. ANy help appreciated.

Thanks
Ian
 
J

John W. Krahn

IanW said:
I have some strings like this (the print_r output from PHP):

my $str = "Array
(
[SY] => 1
[DN] => 1
[YO] => 1
[MK] => 1
[N] => 9
[HP] => 2
[LA] => 1
[NP] => 1
[SW] => 3
)";

I want to be able to parse that string into a Perl hash where the keys are
the letters inside the square brackets (eg: SY or N) and the values are the
numbers after the => (they will always be numeric).

I feel this should be achievable with a cunning one liner using the map
function along with some regexp output (/\[(\w+)\]\D+?(\d+)/) but I don't
know how to begin. ANy help appreciated.

$ perl -le'
my $str = "Array
(
[SY] => 1
[DN] => 1
[YO] => 1
[MK] => 1
[N] => 9
[HP] => 2
[LA] => 1
[NP] => 1
[SW] => 3
)";

my %hash = $str =~ /\[(\w+)\]\D*(\d+)/g;

use Data::Dumper;
print Dumper \%hash;
'
$VAR1 = {
'LA' => '1',
'N' => '9',
'NP' => '1',
'SW' => '3',
'YO' => '1',
'SY' => '1',
'DN' => '1',
'HP' => '2',
'MK' => '1'
};



John
 
J

Josef Moellers

IanW said:
Hi

I have some strings like this (the print_r output from PHP):

my $str = "Array
(
[SY] => 1
[DN] => 1
[YO] => 1
[MK] => 1
[N] => 9
[HP] => 2
[LA] => 1
[NP] => 1
[SW] => 3
)";

I want to be able to parse that string into a Perl hash where the keys are
the letters inside the square brackets (eg: SY or N) and the values are the
numbers after the => (they will always be numeric).

I feel this should be achievable with a cunning one liner using the map
function along with some regexp output (/\[(\w+)\]\D+?(\d+)/) but I don't
know how to begin. ANy help appreciated.

First the obvious question: What have you tried so far and where did
your code not meet your expectations?

Why do you want a "cunning one liner"? What's so bad about

my %h;
foreach (split(/\n/, $str)) {
next unless /\[(\w{1,2})\]\s+=>\s+(\d+)/;
$h{$1} = $2;
}

"cunning one liner"s often turn out to be "obfuscated one liners" one
day, that may cost you some time to figure out.

Josef
 
I

IanW

my %hash = $str =~ /\[(\w+)\]\D*(\d+)/g;

Thanks, I didn't know you could do that. How about if one has more than one
string to parse, like:

my $str_A = "Array
(
[SY] => 4
[DN] => 2
[YO] => 22
}

my $str_B = "Array
(
[SY] => 3
[DN] => 1
[YO] => 11
}

... AND you wanted to build a master hash so that the key is still the
letters within the square brackets but the values go into sub-hashes.
E.g: you would get a dump something like:

$VAR1 = {
SY => {
A => 4,
B => 3
},
DN => {
A => 2,
B => 1
},
YO => {
A => 22,
B => 11
}
};

Can each individual string be parsed into %hash in one line? Perhaps this is
where map comes in?

I've got nothing against using a loop, it's just I would like to know if
it's possible and I might learn something more about parsing strings in the
same way I just learnt that technique you gave for a standard hash.

Regards
Ian
 
S

sln

my %hash = $str =~ /\[(\w+)\]\D*(\d+)/g;

Thanks, I didn't know you could do that. How about if one has more than one
string to parse, like:

my $str_A = "Array
(
[SY] => 4
[DN] => 2
[YO] => 22
}

my $str_B = "Array
(
[SY] => 3
[DN] => 1
[YO] => 11
}

.. AND you wanted to build a master hash so that the key is still the
letters within the square brackets but the values go into sub-hashes.
E.g: you would get a dump something like:

$VAR1 = {
SY => {
A => 4,
B => 3
},
DN => {
A => 2,
B => 1
},
YO => {
A => 22,
B => 11
}
};

Can each individual string be parsed into %hash in one line? Perhaps this is
where map comes in?
No it can't.

sln

--------------------------------------------
use strict;
use warnings;


my $strA = "Array
(
[SY] => 1
[DN] => 1
[YO] => 1
[MK] => 1
[N] => 9
[HP] => 2
[LA] => 1
[NP] => 1
[SW] => 3
)";
my $strB = "Array
(
[SY] => 11
[DN] => 11
[YO] => 11
[MK] => 11
[N] => 91
[HP] => 21
[LA] => 11
[NP] => 11
[SW] => 31
)";

my @Sarray = (\$strA, \$strB);
my %Hash = ();
my $subkey = 'A';

for my $strref (@Sarray)
{
my $href = {$$strref =~ /\[(\w+)\]\D*(\d+)/g};
while (my ($key,$value) = each %{$href}) {
$Hash{$key}->{$subkey} = $value;
}
$subkey++;
}

use Data::Dumper;
print Dumper \%Hash;

__END__

$VAR1 = {
'LA' => {
'A' => '1',
'B' => '11'
},
'SW' => {
'A' => '3',
'B' => '31'
},
'YO' => {
'A' => '1',
'B' => '11'
},
'N' => {
'A' => '9',
'B' => '91'
},
'DN' => {
'A' => '1',
'B' => '11'
},
'SY' => {
'A' => '1',
'B' => '11'
},
'NP' => {
'A' => '1',
'B' => '11'
},
'HP' => {
'A' => '2',
'B' => '21'
},
'MK' => {
'A' => '1',
'B' => '11'
}
};
 
E

Eric Pozharski

*SKIP*
.. AND you wanted to build a master hash so that the key is still the
letters within the square brackets but the values go into sub-hashes.
E.g: you would get a dump something like: *SKIP*
Can each individual string be parsed into %hash in one line? Perhaps this is
where map comes in?

No, this is where C<qr/(?{})/> comes in (in case you're going to stay in
world of obfuscated regexps). F<perlre> has more.

*CUT*
 
S

sln

my %hash = $str =~ /\[(\w+)\]\D*(\d+)/g;

Thanks, I didn't know you could do that. How about if one has more than one
string to parse, like:

my $str_A = "Array
(
[SY] => 4
[DN] => 2
[YO] => 22
}

my $str_B = "Array
(
[SY] => 3
[DN] => 1
[YO] => 11
}

.. AND you wanted to build a master hash so that the key is still the
letters within the square brackets but the values go into sub-hashes.
E.g: you would get a dump something like:

$VAR1 = {
SY => {
A => 4,
B => 3
},
DN => {
A => 2,
B => 1
},
YO => {
A => 22,
B => 11
}
};

Can each individual string be parsed into %hash in one line? Perhaps this is
where map comes in?

I've got nothing against using a loop, it's just I would like to know if
it's possible and I might learn something more about parsing strings in the
same way I just learnt that technique you gave for a standard hash.

Regards
Ian

This would work as well if parsing from a file.

sln
-------------------------------------------

use strict;
use warnings;

my $String = join '', <DATA>;

my %Hash = ();
my $subkey = 'A';

while ( $String =~ /Array\s*\(([^)]*)/g )
{
my $str = $1;
my $href = {$str =~ /\[(\w+)\]\D*(\d+)/g};
while (my ($key,$value) = each %{$href}) {
$Hash{$key}{$subkey} = $value;
}
$subkey++;
}

use Data::Dumper;
print Dumper \%Hash;


__DATA__

Array
(
[SY] => 1
[DN] => 1
[YO] => 1
[MK] => 1
[N] => 9
[HP] => 2
[LA] => 1
[NP] => 1
[SW] => 3
)
Array
(
[SY] => 11
[DN] => 11
[YO] => 11
[MK] => 11
[N] => 91
[HP] => 21
[LA] => 11
[NP] => 11
[SW] => 31
)
 
T

Tad J McClellan

Do you have permission to use that domain from its Korean owner?

If not, then you should use a more polite form of address munging,
perhaps "(e-mail address removed)" or some such.

"John W. Krahn" <[email protected]> wrote in message
^^^^^^^^^^^

Note that the domain that John uses for munging is a special
domain created just for such things.

my %hash = $str =~ /\[(\w+)\]\D*(\d+)/g;

Thanks, I didn't know you could do that.


perldoc perlop

documents what happens when m// is used in a list context:

m// in list context returns a list consisting of the
subexpressions matched by the parentheses in the pattern

How about if one has more than one
string to parse, like:

my $str_A = "Array
(
[SY] => 4
[DN] => 2
[YO] => 22
}


Where is the matching double quote character?

Where is the closing parenthesis?

Where is the opening curly brace?

Please post Real Perl Code that you have copy/pasted rather
than attempt to retype it.

Have you seen the Posting Guidelines that are posted here frequently?

my $str_B = "Array
(
[SY] => 3
[DN] => 1
[YO] => 11
}

.. AND you wanted to build a master hash so that the key is still the
letters within the square brackets but the values go into sub-hashes.
E.g: you would get a dump something like:

$VAR1 = {
SY => {
A => 4,
B => 3
},
DN => {
A => 2,
B => 1
},
YO => {
A => 22,
B => 11
}
};


You have chosen an extremely poor data structure for your strings.

We are expected to get the "A" and "B" from the _name_ of a variable?

Down that path lies madness.

I will use a hash with "A" and "B" keys to hold the two strings instead.

Can each individual string be parsed into %hash in one line?


I don't know, nor do I care.

Obsession with one-liners is unhealthy in production code.

I'll do it with 3 easily-understood lines rather than construct
an infathomable one-liner.


-------------------------
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

my %strings = (
A => "Array
(
[SY] => 4
[DN] => 2
[YO] => 22
)",
B => "Array
(
[SY] => 3
[DN] => 1
[YO] => 11
)",
);

my %result;
foreach my $str ( keys %strings ) {
while ( $strings{$str} =~ /\[(\w+)] => (\d+)/g ) {
$result{$1}{$str} = $2;
}
}

print Dumper \%result;
 
S

sln

my %hash = $str =~ /\[(\w+)\]\D*(\d+)/g;

Thanks, I didn't know you could do that. How about if one has more than one
string to parse, like:

my $str_A = "Array
(
[SY] => 4
[DN] => 2
[YO] => 22
}

my $str_B = "Array
(
[SY] => 3
[DN] => 1
[YO] => 11
}

.. AND you wanted to build a master hash so that the key is still the
letters within the square brackets but the values go into sub-hashes.
E.g: you would get a dump something like:

$VAR1 = {
SY => {
A => 4,
B => 3
},
DN => {
A => 2,
B => 1
},
YO => {
A => 22,
B => 11
}
};

Can each individual string be parsed into %hash in one line? Perhaps this is
where map comes in?

I've got nothing against using a loop, it's just I would like to know if
it's possible and I might learn something more about parsing strings in the
same way I just learnt that technique you gave for a standard hash.

Regards
Ian

Adding one more comment. In reality, anything can be parsed.
The most aggregious things that sticks out in your post is the
attempt to write a pseudo hash structure within a string that
is hardcoded in a Perl script. Maybe to show people your intentions,
I don't know.

You stated it is generated (from php?). The data and the processing code
is that what you mean?

There is no variablity, you might as well construct the hash your
looking for by hand, its %90 there in your statics.

Maybe your just looking for clues in the details. However, your not
rational in your approach. Usually, I draw the line with impractical
premise. But I thought you might be looking for elements in a bigger
picture.

Don't sell the responders here short, clearly state your intentions!

sln
 
H

Hans Mulder

IanW said:
Can each individual string be parsed into %hash in one line

Creating one-liners in Perl is easy.
Just apply this to your script:

#!/usr/bin/perl
$_=<>;if(/^#!/){print;$_='';}undef$/;$_.=<>;
s/([\$\@\\#])/\\$1/g;s/\n/\\n/g;print"eval qq#$_#;\n";

It'll produce a one-liner version of your script.

Readability not included.

Disclaimer: I didn't write this thing myself; I just found it
on Usenet. I think is was originally written by Larry Wall.

-- HansM
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,007
Messages
2,570,267
Members
46,866
Latest member
Aletlirm

Latest Threads

Top