splitting to a hashref

K

Keith

I'm getting myself all confused - nothing new there.

I'm building a data structure something like this (greatly
simplified):

my %company;
..
..
$company{company_code} = {
classes => [ split ('/',
$record->{class_codes}) ]
}

The incoming data field 'class_codes' is structured like this:

"CLASS1/CLASS2/CLASS3/CLASS7/CLASS9"

This means my data structure ends up with an entry called 'classes'
which holds an array reference to a list of class codes. What I'd
really like is that entry to hold a hash reference where each key is a
class code, and the corresponding value is 'Y' (for example).

I did try changing that line to read:
classes => { split ('/',
$record->{class_codes}) => 'Y' }

But that gives me this:
$VAR1 = {
'classes' => {
'CLASS9' => 'Y',
'CLASS3' => 'CLASS7',
'CLASS1' => 'CLASS2'
}
};

I'm missing something fundamental about slices here, I guess. Please
help!
 
T

Tim McDaniel

I'm building a data structure something like this (greatly
simplified):

my %company;
.
.
$company{company_code} = {
classes => [ split ('/',$record->{class_codes}) ]
}

(I removed massive whitespace)
The incoming data field 'class_codes' is structured like this:

"CLASS1/CLASS2/CLASS3/CLASS7/CLASS9"

This means my data structure ends up with an entry called 'classes'
which holds an array reference to a list of class codes. What I'd
really like is that entry to hold a hash reference where each key is
a class code, and the corresponding value is 'Y' (for example).

I did try changing that line to read:
classes => { split ('/', $record->{class_codes}) => 'Y' }

But that gives me this:
$VAR1 = {
'classes' => {
'CLASS9' => 'Y',
'CLASS3' => 'CLASS7',
'CLASS1' => 'CLASS2'
}
};

I'm missing something fundamental about slices here, I guess. Please
help!

Perl isn't quite as slicy as you'd like. Also, => is not a special
pairing operator; it's just a fancy-pants way of writing ",". So the
guts are equivalent to

{ split ('/', $record->{class_codes}), 'Y' }

which evaluates to a list of just the class codes, followed by one
"Y", so it's just

{ 'CLASS1', 'CLASS2', 'CLASS3', 'CLASS7', 'CLASS9', 'Y' }

{...}, or other uses of lists where it's expecting a hash, then does
the pairing, using alternating elements of the list as key and value
in that order. That produces your result (out of alphabetical order
because Perl hashes do that).

Perl doesn't provide the pairing you want, so you have to do it by
hand:

classes => { map { $_, 'Y' } split ('/', $record->{class_codes}) }

(Most people would actually write "$_ => 'Y'", but I want to emphasize
to you that => really IS a comma, and what's important is having
key followed by value.)

{} have two different meanings here. {} in map{} are code block
delimiters, like if{} or sub{}. It's the outer {...} that make an
anonymous hash ref.

Or you could assign to a temp array and do a foreach, or do something
involving
@classes{@classes_array} = ('Y') x scalar @classes_array;
but that map version is really simplest.
 
C

ccc31807

I'm getting myself all confused - nothing new there.

Do it in small, simple steps, like this. Test each function separately
to see that it does what you expect. There are more sophisticated ways
to do this (e.g., Data::Dumper, Text::parseWords, etc.) but this will
do what you want.

#! perl
use strict;
use warnings;
my (%in_data, %out_data);
open_file_and_build_in_data();
munge_in_data_to_out_data();
test_hashes();
write_out_data();
exit(0);

open_file_and_build_in_data
{
open IN, '<', 'in_data.dat' or die "$!";
while (<IN>)
{
chomp;
my ($val1, $val2, $val3 ...) = split(/\/\, $));
my $in_data{$val1} = {
val2 => $val2,
val3 => $val3,
... };
}
close IN;
}
munge_in_data_to_out_data
{
foreach my $key (keys %in_data)
{
#iterate through the keys building your out data
}
}
test_hashes
{
#just for sanity's sake
foreach my $key1 (keys %in_data)
{
print "$key1 =>\n";
foreach my $key2 (keys %{$in_data{$key1}})
{
print " $key2 => $in_data{$key1}{$key2}\n";
}
}
foreach my $key1 (keys %out_data)
{
print "$key1 =>\n";
foreach my $key2 (keys %{$out_data{$key1}})
{
print " $key2 => $out_data{$key1}{$key2}\n";
}
}
}
}
write_out_data
{
open OUT, '>', 'out_data.dat' or die "$!":
foreach my $key1 (keys %out_data)
{
foreach my $key2 (keys %{$out_data{$key1}})
{
print OUT qq($key1,$out_data{$key1}{$key2} ... \n);
}
close OUT;
}
}
 
W

Willem

Keith wrote:
) This means my data structure ends up with an entry called 'classes'
) which holds an array reference to a list of class codes. What I'd
) really like is that entry to hold a hash reference where each key is a
) class code, and the corresponding value is 'Y' (for example).
)
) I did try changing that line to read:
) classes => { split ('/',
) $record->{class_codes}) => 'Y' }
)
) But that gives me this:
) $VAR1 = {
) 'classes' => {
) 'CLASS9' => 'Y',
) 'CLASS3' => 'CLASS7',
) 'CLASS1' => 'CLASS2'
) }
) };
)
) I'm missing something fundamental about slices here, I guess. Please
) help!

I have no idea what this has to do with slices, but if you want to
transform an array into a hash, you need to use 'map':

classes => { map { $_ => 'Y' } split '/', ... }

Which will do what you wanted.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
G

George Mpouras

# I want to answer this !

my %company;
my $record = {};

$record->{class_codes} = "CLASS1/CLASS2/CLASS3/CLASS7/CLASS9";
$company{company_code} = { classes => Doit($record->{class_codes}) };

use Data::Dumper; print Dumper(\%company);

sub Doit {
local $_=[[split/\//,$_[0]],{}];
for (my($i,$j)=(0,1);$i<=$#{$_->[0]};$i+=2,$j+=$i) {
exists $_->[0][$j] ? ($_->[1]{$_->[0][$i]}=$_->[0][$j] ):(
$_->[1]{$_->[0][$i]} = 'Y' ) }
$_->[1]
}
 
G

George Mpouras

# Sorry , you need this one

my %company;
my $record = {};

$record->{class_codes} = "CLASS1/CLASS2/CLASS3/CLASS7/CLASS9";
$company{company_code} = { classes => eval{my$H; $H->{$_}='Y' foreach
split/\//,$record->{class_codes};$H} };

use Data::Dumper; print Dumper(\%company);
 
T

Tim McDaniel

# Sorry , you need this one

my %company;
my $record = {};

$record->{class_codes} = "CLASS1/CLASS2/CLASS3/CLASS7/CLASS9";
$company{company_code} = { classes => eval{my$H; $H->{$_}='Y' foreach
split/\//,$record->{class_codes};$H} };

use Data::Dumper; print Dumper(\%company);

If I may comment:

Use of whitespace and indentation can REALLY help readability. I'm
sorry, but I think the above is a good entry in the Obfuscated Perl
Contest.

$record->{class_codes} = "CLASS1/CLASS2/CLASS3/CLASS7/CLASS9";
$company{company_code} = {
classes => eval {
my $H;
$H->{$_}='Y' foreach split /\//, $record->{class_codes};
$H
}
};
use Data::Dumper; print Dumper(\%company);

We had a discussion in the past few weeks about how to put code blocks
inside expressions. One major opinion is "don't". But if you're
going to do it, I think "do { ... }" is better than "eval { ... }",
because if anything dies inside, "eval" will swallow the error unless
you code to expose it (and you need to be more careful about that
depending on the version of Perl).

Looking at that, I still prefer

$company{company_code} = {
classes => { map { $_, 'Y' } split /\//, $record->{class_codes} }
};

And
@{$company{company_code}{classes}}[split /\//, $record->{class_codes}] =
('Y') x length $record->{class_codes};
is too silly and too obscure.
 
K

Keith

$company{company_code} = {
    classes => { map { $_, 'Y' } split /\//, $record->{class_codes}}

My thanks to all who explained this so clearly. I've got it now, and
as a pennance, I ahall learn all about the uses of the 'map' command!
 

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

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top