Home > Archive > PERL Beginners > September 2006 > hash slices
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
|
|
| Gerald Host 2006-09-28, 6:57 pm |
| I'm using DBI's selectall_hashref with 5 key columns. I want to display
each row where key col 1 is 'yes' or NULL (undef or '' ?)
foreach my $medCategory (keys
%{$href->{'yes'}->{qw('' 'yes')}->{qw('' 'yes')}}) {
foreach my $med (keys
%{$href->{qw('yes')}->{qw('' 'yes')}->{qw('' 'yes')}->{$medCategory}}) {
foreach my $ID (keys
%{$href->{qw('yes')}->{qw('' 'yes')}->{qw(''
'yes')}->{$medCategory}->{$med}}) {
print qq{ $href->{qw('yes')}->{qw(''
'yes')}->{qw('' 'yes')}->{$medCategory}->{$med}->{$ID}->{ID} };
print qq{ $href->{qw('yes')}->{qw(''
'yes')}->{qw('' 'yes')}->{$medCategory}->{$med}->{$ID}->{Medication} };
}
}
}
This isn't working of course because I don't quite understand the syntax I
need. Can someone give me a hint? Thanks!
Ryan
| |
| Rob Dixon 2006-09-28, 6:57 pm |
| Gerald Host wrote:
>
> I'm using DBI's selectall_hashref with 5 key columns. I want to display
> each row where key col 1 is 'yes' or NULL (undef or '' ?)
>
>
> foreach my $medCategory (keys %{$href->{'yes'}->{qw('' 'yes')}->{qw(''
'yes')}}) {
> foreach my $med (keys %{$href->{qw('yes')}->{qw('' 'yes')}->{qw(''
'yes')}->{$medCategory}}) {
> foreach my $ID (keys %{$href->{qw('yes')}->{qw('' 'yes')}->{qw(''
'yes')}->{$medCategory}->{$med}}) {
> print qq{ $href->{qw('yes')}->{qw('' 'yes')}->{qw(''
'yes')}->{$medCategory}->{$med}->{$ID}->{ID} };
> print qq{ $href->{qw('yes')}->{qw('' 'yes')}->{qw(''
'yes')}->{$medCategory}->{$med}->{$ID}->{Medication} };
> }
> }
> }
>
> This isn't working of course because I don't quite understand the syntax I
> need. Can someone give me a hint? Thanks!
>
> Ryan
Ryan/Gerald (!)
You don't want to use selectall_hashref because, as is the nature of hashes, the
key must be unique, which means the database table's key field that provides it
must also be unique. If you get this working, you will retrieve a single record
for each possible value of each key: presumably 'yes', 'no', '', and NULL or
something like that.
It does look though as if you're pulling all your database data into a Perl hash
and trying to interrogate that. Surely, in your example above, you should be
writing somthing such as:
my $data = $dbh->selectall_arrayref(qq(
SELECT id, medication
FROM table
WHERE col1 = 'yes' OR OR col1 = '' OR col1 IS NULL
ORDER BY id
));
foreach my $row (@$data) {
printf "%s: %s\n", @$row;
}
(I don't know what the 'yes' field's name is, or what the table name is.)
By the way, qw doesn't work like that - you've used it as a tool that just
allows you to miss out the commas.
qw(yes no maybe)
is the same as
('yes', 'no', 'maybe')
i.e. its effect is the same as a call to split() on the string within the
delimiters. There's no way no write the empty string using this of course, so
your
qw('' 'yes')
should be
('', 'yes')
but that won't make the program work!
Hope this helps,
Rob
| |
| Gerald Host 2006-09-28, 6:57 pm |
| >
>
> Ryan/Gerald (!)
(Ryan)
You don't want to use selectall_hashref because, as is the nature of hashes,
> the
> key must be unique, which means the database table's key field that
> provides it
> must also be unique. If you get this working, you will retrieve a single
> record
> for each possible value of each key: presumably 'yes', 'no', '', and NULL
> or
> something like that.
I know. One of my key columns is an ID field so there will be a unique key.
It does look though as if you're pulling all your database data into a Perl
> hash
> and trying to interrogate that. Surely, in your example above, you should
> be
> writing somthing such as:
>
> my $data = $dbh->selectall_arrayref(qq(
> SELECT id, medication
> FROM table
> WHERE col1 = 'yes' OR OR col1 = '' OR col1 IS NULL
> ORDER BY id
> ));
>
> foreach my $row (@$data) {
> printf "%s: %s\n", @$row;
> }
nope:
my $href =$$dbh->selectall_hashref(q{SELECT * FROM recommendations WHERE
RxNumber=? ORDER BY medCategory, Medication, methodPreference}, [qw(
medCategory Medication include ifFollowedProtocol ifCycling ID) ],
undef,('12345'));
So I'm still about what I need to do. I want to:
1. display the rows with ifFollowedProtocol eq '' or 'yes' in order of
medCategory
2. display the rows with ifFollowedProtocol eq '' or 'no' in order of
medCategory
I thought I could do this with a hash of hashes...I know there are other
ways, but I'd like to understand how to do it with this hashref using hash
slices if it's possible.
Thanks!
| |
| John W. Krahn 2006-09-28, 6:57 pm |
| Gerald Host wrote:
> I'm using DBI's selectall_hashref with 5 key columns. I want to display
> each row where key col 1 is 'yes' or NULL (undef or '' ?)
>
>
> foreach my $medCategory (keys %{$href->{'yes'}->{qw('' 'yes')}->{qw('' 'yes')}}) {
> foreach my $med (keys %{$href->{qw('yes')}->{qw('' 'yes')}->{qw('' 'yes')}->{$medCategory}}) {
> foreach my $ID (keys %{$href->{qw('yes')}->{qw('' 'yes')}->{qw('' 'yes')}->{$medCategory}->{$med}}) {
>
> print qq{ $href->{qw('yes')}->{qw('' 'yes')}->{qw('' 'yes')}->{$medCategory}->{$med}->{$ID}->{ID} };
> print qq{ $href->{qw('yes')}->{qw('' 'yes')}->{qw('' 'yes')}->{$medCategory}->{$med}->{$ID}->{Medication} };
> }
> }
> }
>
> This isn't working of course because I don't quite understand the syntax I
> need. Can someone give me a hint? Thanks!
A slice of a variable has to start with @, for example:
@array_slice[ 1 .. 9 ];
@hash_slice{ 'one', 'two', 'three' };
If you have a multidimensional data structure you can only slice at the end:
@{ $array_ref->[ 3 ][ 7 ] }[ 1 .. 9 ];
@{ $hash_ref->{ x }{ y } }{ 'one', 'two', 'three' };
In your example:
%{ $href->{ 'yes' }->{ qw( '' 'yes' ) }->{ qw( '' 'yes' ) } }
There is no slice, and the lists evaluate to the last item in the list so what
you actually have is:
%{ $href->{ 'yes' }->{ "'yes'" }->{ "'yes'" } }
And yes, the single quotes are included in the key as that is the way qw() works.
It looks like you want something like this:
for my $href1 ( @{ $href->{ yes } }{ '', 'yes' } ) {
for my $href2 ( @{ $href1 }{ '', 'yes' } ) {
for my $medCategory ( keys %$href2 ) {
for my $med ( keys %{ $href2->{ $medCategory } } ) {
for my $ID ( keys %{ $href2->{ $medCategory }{ $med } } ) {
print " $href2->{ $medCategory }{ $med }{ $ID }{ ID } ";
print " $href2->{ $medCategory }{ $med }{ $ID }{
Medication } ";
}
}
}
}
}
John
--
Perl isn't a toolbox, but a small machine shop where you can special-order
certain sorts of tools at low cost and in short order. -- Larry Wall
| |
| Rob Dixon 2006-09-28, 6:57 pm |
| Gerald Host wrote:
>
> Rob Dixon wrote:
>
>
> I know. One of my key columns is an ID field so there will be a unique key.
It doesn't work like that. The hash level corresponding to ifFollowedProtocol
must be unique on its own, It's independent of anything in the nested hashes and
(in your code below) anything with the same 'medCategory' 'Medication' 'include'
and 'ifFollowedProtocol' will try to occupy the same slot in the hash at that
level and previous stuff will be overwritten.
By the way, is your list of key fields below correct? You said you had five key
columns in your original post.
>
>
>
> nope:
>
> my $href =$$dbh->selectall_hashref(q{SELECT * FROM recommendations WHERE
> RxNumber=? ORDER BY medCategory, Medication, methodPreference}, [qw(
> medCategory Medication include ifFollowedProtocol ifCycling ID) ],
> undef,('12345'));
OK, that's a little clearer. But why so many key fields? In fact why not just
ID, since it's a unique one. By the way, an ORDER BY is pointless with
selectall_hashref, as hashes are unordered.
> So I'm still about what I need to do. I want to:
>
> 1. display the rows with ifFollowedProtocol eq '' or 'yes' in order of
> medCategory
> 2. display the rows with ifFollowedProtocol eq '' or 'no' in order of
> medCategory
>
> I thought I could do this with a hash of hashes...I know there are other
> ways, but I'd like to understand how to do it with this hashref using hash
> slices if it's possible.
Alright, lets try.two solutions. First mine, which seems to do what you want,
but has only one level of hash so is a lot easier to code for
my $href = $dbh->selectall_hashref(q{
SELECT *
FROM recommendations
WHERE RxNumber = ?
}, 'ID', undef, 12345);
foreach my $id (sort keys %$href) {
my $data = $href->{$id};
my $ifp = $data->{ifFollowedProtocol};
next if defined $ifp and $ifp ne '' and $ifp ne 'yes';
print $data->{ID}, "\n";
print $data->{Medication}, "\n";
}
And now one with all the key fields in as you had, and a hash slice
my $medcat_href = $dbh->selectall_hrefref(q{
SELECT *
FROM recommendations
WHERE RxNumber = ?},
[qw(
medCategory
Medication
include
ifFollowedProtocol
ifCycling
ID
)],
undef,('12345'));
foreach my $med_href (values %$medcat_href) {
foreach my $include_href (values %$med_href) {
foreach my $protocol_href (values %$include_href) {
foreach my $cycling_href (@{$protocol_href}{'yes', ''}) {
foreach my $id_href (values %$cycling_href) {
foreach my $data (values %$id_href) {
print $data->{ID}, "\n";
print $data->{Medication}, "\n";
}
}
}
}
}
}
which I've simplified from your code by taking the hash references out at each
stage instead of applying the full sequence of keys relative to the root
$medcat_href. Also, if you put the ID key first in the list, the problem of
duplicate 'ifFollowedProtocol' values is removed, but then the whole idea of
putting multiple keys in here becomes obviously pointless as every hash wihtin
the outermost one will have only a single key/value entry.
Now I expect I've misunderstood something here, but I hope some of this is of
some use to get you on track? Let us know.
Rob
|
|
|
|
|