Re: Perl style... help me figure this out.
by ikegami (Patriarch) on Feb 07, 2012 at 00:56 UTC
|
my %items = map { $_ => 1 } (
'APPLES',
'SILVER',
);
my %groups = (
'cookies' => ['CHOCOLATE CHIP', 'PEANUT BUTTER'],
'previous metals' => ['SILVER', 'GOLD', 'PLATINUM'],
);
my $lover;
for my $group_name (keys(%groups)) {
if ( grep $items{$_}, @{ $groups{$group_name} } ) {
print "This guy loves $group_name.\n";
++$lover;
}
}
if (!$lover) {
print "This guy loves nothing.\n";
}
| [reply] [d/l] |
|
|
Agreed upon the direly needed improvements. This is definitely a step in the right direction, but it strays slightly from the technical requirements which I failed to communicate.
-Ordering is important. Does this indicate that I should be using an AoA?
-I would like to preserve the key-value relationship if at all possible.
| [reply] |
|
|
Yeah, you can use an array instead of a hash.
my %items = map { $_ => 1 } (
'APPLES',
'SILVER',
);
my @groups = (
[ 'cookies', [ 'CHOCOLATE CHIP', 'PEANUT BUTTER' ] ],
[ 'previous metals', [ 'SILVER', 'GOLD', 'PLATINUM' ] ],
);
my $lover;
for my $group (@groups) {
my ($group_name, $group_members) = @$group;
if ( grep $items{$_}, @$group_members ) {
print "This guy loves $group_name.\n";
++$lover;
}
}
if (!$lover) {
print "This guy loves nothing.\n";
}
| [reply] [d/l] |
|
|
EDIT: I should have made it clear that ordering is important and that 'this guy' is a simple man and can only love one thing. I know that this hints that I should be using an array of arrays instead of a hash of arrays, but the key-value relationship is something I would like to preserve if at all possible.
After reading your EDIT, I am confused as to what the requirements really are. What does "ordering is important" mean? Order of "what" exactly?
A hash has no intrinsic "order" to the keys - you should assume that hash keys will come out in any order when you access a hash via (keys %hash) or (keys %$ref_2_hash).
It would help at least me, if you could back up a bit.. Write some more text describing what data you have and the kind of "look-up" that you desire. It could be that excellent solutions are being offered to the "wrong problem".
| [reply] |
Re: Perl style... help me figure this out.
by Marshall (Canon) on Feb 07, 2012 at 09:31 UTC
|
I am trying to steer clear from arrays and grep.
These should be your "buddies"!
For a "list" of stuff, ordered or not, think: "array".
For a subset of an array, think: "grep"
The code below uses an uncommon feature, a label. However it seemed to be appropriate in this situation.
"Seldom used" doesn't mean "never used".
#!/usr/bin/perl -w
use strict;
my @values = ('CHOCOLATE CHIP', 'PLATINUM', 'GASOLINE', 'GOLD');
my %hash = (
'COOKIE' => ['CHOCOLATE CHIP', 'PEANUT BUTTER'],
'METAL' => ['SILVER', 'GOLD', 'PLATINUM'],
);
THING: foreach my $thing (@values)
{
foreach my $type (keys %hash)
{
if (grep{$_ eq $thing}@{$hash{$type}} )
{
print "$thing is a $type\n";
next THING;
}
}
print "$thing is UNKOWN\n";
}
__END__
CHOCOLATE CHIP is a COOKIE
PLATINUM is a METAL
GASOLINE is UNKOWN
GOLD is a METAL
Update: UNKOWN, UNKNOWN is a typo..means nothing in the sense of the code. These things happen... | [reply] [d/l] |
Re: Perl style... help me figure this out.
by mcdave (Beadle) on Feb 07, 2012 at 04:25 UTC
|
So, you've got %$hash whose values are arrayrefs, and you've got %$values whose keys are things you're looking for, and you want to find those keys $k in %$hash such that @{$hash->{$k}} contains a key from %$values. Is that the problem you're trying to solve?
Assuming it is, then it seems grep and array are pretty much what you're after (try as you might to avoid them), since you're asking for $k such that grep { $values->{$_} } @{$hash->{$k}} is nonempty.
OK, this isn't going to work that well for large lists, because grep can't short-circuit and you'll waste time after you "already" know the answer is yes. Maybe wait for Perl 6? No, probably not a good idea. So you'll need to do your own short-circuiting by iterating over @{$hash->{$k}} and quitting when you've found something that matches.
Since nothing is sorted anywhere in this exercise, I don't think you'll do much better than "iterate over the keys of %$hash, then iterate over the entries in the arrayref and check each one". | [reply] [d/l] [select] |
|
|
| [reply] [d/l] [select] |
Re: Perl style... help me figure this out.
by roboticus (Chancellor) on Feb 07, 2012 at 11:48 UTC
|
jaydstein:
If you're trying to determine the category of a particular item, and you're doing it frequently in your code, then you might want to use a reverse lookup table (inverted hash). Basically, it's just a hash where each value points to the category name in your list:
$ cat 952190.pl
#!/usr/bin/perl
use strict;
use warnings;
my $hash = {
'COOKIES' => ['CHOCOLATE CHIP', 'PEANUT BUTTER', 'OATMEAL'],
'METALS' => ['SILVER', 'GOLD', 'PLATINUM'],
'BREAKFAST' => ['OATMEAL', 'WAFFLES', 'CEREAL'],
};
my $values = {
'COOKIES' => '1',
'SILVER' => '1',
};
my $revLookup;
for my $k (%$hash) {
for my $v (@{$$hash{$k}}) {
$$revLookup{$v} = $k;
}
}
print who_do_ya_love('CHOCOLATE CHIP');
print who_do_ya_love('PLATINUM');
print who_do_ya_love('OATMEAL');
sub who_do_ya_love {
my $item = shift;
my $category = $$revLookup{$item} // 'NOTHING';
"This guy loves $category\n";
}
$ perl 952190.pl
This guy loves COOKIES
This guy loves METALS
This guy loves BREAKFAST
The downsides of using an inverted hash:
- You actually have to build the inverted hash: That can be expensive for large quantities of data.
- Each time your list of values changes, you have to build it again--or add code to maintain the reverse hash with the rest of the data.
- Since values may be in multiple lists, you'll have to either:
- (a) bear with it (as I did in this example), or
- (b) decide how to handle (and then write code for) multiple lists.
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [d/l] |
Re: Perl style... help me figure this out.
by Riales (Hermit) on Feb 07, 2012 at 00:59 UTC
|
May I ask why you're trying to steer clear from arrays (I'm assuming you mean loops instead of arrays) and grep?
Also, I'm pretty sure the code you provided does not run. Your $hash hashref has keys that point to arrayrefs, so you would need to dereference them like so:
print $hash->{COOKIES}->[0]; # Gives you: CHOCOLATE CHIP
EDIT: I stand corrected. $hash->{COOKIES}[0] works just fine. Seems weird to me, but there you go. | [reply] [d/l] [select] |
|
|
That's quite simple. If the only thing you could ever write between the curly and the square bracket is a -> and you have to write it always, then why should you have to write it at all?
It's a convenient shortcut that may be used no matter whether you are chaining array or hash subscriptions. $AoA[1][4], $HoH{some}{thing}, $AoH[1]{whatever}, $HoA{barf}[42], ...
The arrow at the beginning though is important. $A[0] is the first element of the array @A, $A->[0] on the other hand is the first element of the array referenced by $A. Because arrays and hashes may only store scalars (strings, numbers or references) you do not need the arrow between the subscriptions to distinguish such two cases.
Jenda
Enoch was right!
Enjoy the last years of Rome.
| [reply] [d/l] [select] |
|
|
| [reply] |