I have a key-value hash where all keys and values are codes relating to either apples, oranges or lemons. I want to find how many orange-apple, apple-lemon and lemon-orange pairs i have in the hash. All the codes relating to each fruit group are stored in seperate arrays.
However, I could really use some help on finding the best way to do this. Ideally I would like an output (preferably the same order as is in the hash) stating whether each key & value are apples, oranges or lemons:
e.g.
#e.g. lemon-lemon apple-orange orange-lemon orange-appleHere is my code so far:
my %hash = map { $keys[$_] => $values[$_] } 0.. $#keys;
for (my $i=0; $i<@apple_ids; $i++) {
while ( my ($key, $value) = each(%hash) ) {
if ($apple_ids[$i] == $key) {
print "apple";
}
if ($apple_ids[$i] == $value) {
print "apple";
}
}
}
for (my $i=0; $i<@orange_ids; $i++) {
while ( my ($key, $value) = each(%hash) ) {
if ($orange_ids[$i] == $key) {
print "orange";
}
if ($orange_ids[$i] == $value) {
print "orange";
}
}
}
for (my $i=0; $i<@lemon_ids; $i++) {
while ( my ($key, $value) = each(%hash) ) {
if ($lemon_ids[$i] == $key) {
print "lemon";
}
if ($lemon_ids[$i] == $value) {
print "lemon";
}
}
}
That you want your output "in the same order as the hash" is a not a good sign. Hashes are unordered. If you want a specific order, then you'll need to specify it each time you access the hash.
That said, here's what I think you want:
my %fruit_map;
@fruit_map{@apple_ids} = ("apple") x @apple_ids;
@fruit_map{@orange_ids} = ("orange") x @orange_ids;
@fruit_map{@lemon_ids} = ("lemon") x @lemon_ids;
while (my($k,$v) = each %hash) {
print "$fruit_map{$k}-$fruit_map{$v}\n";
}
%fruit_map is just a hash that maps the ID to the appropriate fruit given that you have a list of IDs that belong to known fruit. I've used hash-slice syntax and the x operator in list context to set the hash in groups.
[duff] is right on, wrt mapping each id to its fruit class.
Now let's talk about the rest of the problem: checking all the input data, and printing the count summary.
It appears that you don't actually start with a hash, but with two arrays, parallel - one for key, one for value. Converting that into a hash is a mistake, unless you don't care about counts. But you do. So let's just iterate over the input data directly:
my( @keys, @values ); # wherever they come from.
@keys == @values or die "input arrays not same length!";
my %fruit_map; # and populate as [duff] showed.
my %pair_count; # accumulate results
for my $i ( 0 .. $#keys )
{
my $k = $fruit_map{ $keys[$i] };
my $v = $fruit_map{ $values[$i] };
$pair_count{"$k-$v"}++;
}
# now show results:
for ( sort keys %pair_count )
{
print "$_: $pair_count{$_}\n";
}
You can achieve some degree of retaining the input order in the output by tie'ing %pair_count to [mod://Tie::IxHash].
#!/usr/bin/perl
use strict;
use warnings;
# For example...
my $fruits = [];
my $apple = { 'apple' => 1 };
my $orange = { 'orange' => 1 };
my $lemon = { 'lemon' => 1 };
# First, something needs to populate the ref.
# This is just an example, so you will naturally
# have to modify this to fit your app better
$fruits = [ $apple, $orange, $lemon, $orange ];
for my $fruit ( @$fruits ) {
print "$_\n" for keys %$fruit;
}
exit( 0 );
Albeit simplistic for what you are looking for, this may be closer to finding out what value of what is being placed into your 'hash'. Given that arrays are ordered, the array-ref wrapper is perhaps more appropriate to what you are looking for.
print$_ for(map{chr($_)}split(/\s+/,join(/\B?:\w+[^\s+]/,)));
__DATA__
67 111 100 101 32 80 101 114 108
perlmonks.org content © perlmonks.org and Anonymous Monk, duff, jdporter, wazzuteke
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03