Padawan
Padawan

Reputation: 313

Perl Hash Trouble

An easy one for a Perl guru...

I want a function that simply takes in an array of items (actually multiple arrays) and counts the number of times each item in the key section of a hash is there. However, I am really unsure of Perl hashes.

@array = qw/banana apple orange apple orange apple pear/

I read that you need to do arrays using code like this:

my %hash = (
    'banana' => 0,
    'orange' => 0,
    'apple' => 0
    #I intentionally left out pear... I only want the values in the array...
);

However, I am struggling getting a loop to work that can go through and add one to the value with a corresponding key equal to a value in the array for each item in the array.

foreach $fruit (@array) {
    if ($_ #is equal to a key in the hash) {
        #Add one to the corresponding value
    }
}

This has a few basic functions all wrapped up in one, so on behalf of all beginning Perl programmers, thank you in advance!

Upvotes: 1

Views: 205

Answers (5)

Prem
Prem

Reputation: 319

This will be easier to understand as I too started to write code just 2 months back.

use Data::Dumper;
use strict;
use warnings;
my @array = qw/banana apple orange apple orange apple pear/;

my %hashvar;
foreach my $element (@array) {
    #Check whether the element is already added into hash ; if yes increment; else add. 
    if (defined $hashvar{$element}) {  
        $hashvar{$element}++;
    }
    else {
        $hashvar{$element} = 1;

    }

}
print Dumper(\%hashvar);

Will print out the output as $VAR1 = { 'banana' => 1, 'apple' => 3, 'orange' => 2, 'pear' => 1 }; Cheers

Upvotes: 1

David W.
David W.

Reputation: 107090

I'm trying to understand you here:

  1. You have an array and a hash.
  2. You want to count the items in the array and see how many time they occur
  3. But, only if this item is in your hash.

Think of hashes as keyed arrays. Arrays have a position. You can talk about the 0th element, or the 5th element. There is only one 0th element and their is only one 5th element.

Let's look at a hash:

my %jobs;
$jobs{bob} = "banker";
$jobs{sue} = "banker";
$jobs{joe} = "plumber;

Just as we can talk about the element in the array in the 0th position, we can talk about the element with the of bob. Just as there is only one element in the 0th position, there can only be one element with a key of bob.

Hashes provide a quick way to look up information. For example, I can quickly find out Sue's job:

print "Sue is a $jobs{sue}\n";

We have:

  • An array filled with items.
  • A hash with the items we want to count
  • Another hash with the totals.

Here's the code:

use strict;
use warnings;
use feature qw(say);

my @items       = qw(.....);   # Items we want to count
my %valid_items =   (....);    # The valid items we want

# Initializing the totals. Explained below...
my %totals;
map { $totals{$_} = 0; } keys %valid_items;

for my $item ( @items ) {
    if ( exists $valid_items{$item} ) {
        $totals{$item} += 1;  #Add one to the total number of items
    }
}

#
# Now print the totals
#
for my $item ( sort keys %totals ) {
    printf "%-10.10s  %4d\n", $item, $totals{$item};
}

The map command takes the list of items on the right side (in our case keys %valid_items), and loop through the entire list.

Thus:

map { $totals{$_} = 0; } keys %valid_items;

Is a short way of saying:

for ( keys %valid_items ) { $totals{$_} = 0; }

The other things I use are keys which returns as an array (okay list) all of the keys of my hash. Thus, I get back apple, banana, and oranges when I say keys %valid_items.

The [exists](http://perldoc.perl.org/functions/exists.html) is a test to see if a particular key is in my hash. The value of that key might be zero, a null string, or even an undefined value, but if the key is in my hash, theexists` function will return a true value.

However, if we can use exists to see if a key is in my %valid_items hash, we could do the same with %totals. They have the same set of keys.

Instead or creating a %valid_items hash, I'm going to use a @valid_items array because arrays are easier to initialize than hashes. I just have to list the values. Instead of using keys %valid_items to get a list of the keys, I can use @valid_items:

use strict;
use warnings;
use feature qw(say);

my @items = qw(banana apple orange apple orange apple pear);   # Items we want to count
my @valid_items = qw(banana apple orange);    # The valid items we want

my %totals;
map { $totals{$_} = 0; } @valid_items;

# Now %totals is storing our totals and is the list of valid items

for my $item ( @items ) {
    if ( exists $totals{$item} ) {
        $totals{$item} += 1;  #Add one to the total number of items
    }
}

#
# Now print the totals
#
for my $item ( sort keys %totals ) {
    printf "%-10.10s  %4d\n", $item, $totals{$item};
}

And this prints out:

apple          3
banana         1
orange         2

I like using printf for keeping tables nice and orderly.

Upvotes: 1

rutter
rutter

Reputation: 11452

Suppose you have an array named @array. You'd access the 0th element of the array with $array[0].

Hashes are similar. %hash's banana element can be accessed with $hash{'banana'}.

Here's a pretty simple example. It makes use of the implicit variable $_ and a little bit of string interpolation:

use strict;

my @array = qw/banana apple orange apple orange apple pear/;

my %hash;
$hash{$_} += 1 for @array;              #add one for each fruit in the list
print "$_: $hash{$_}\n" for keys %hash; #print our results

If needed, you can check if a particular hash key exists: if (exists $hash{'banana'}) {...}.

You'll eventually get to see something called a "hashref", which is not a hash but a reference to a hash. In that case, $hashref has $hashref->{'banana'}.

Upvotes: 1

Borodin
Borodin

Reputation: 126772

All you need is

my @array = qw/banana apple orange apple orange apple pear/;
my %counts;
++$counts{$_} for @array;

This results in a hash like

my %counts = ( apple => 3, banana => 1, orange => 2, pear => 1 )

The for loop can be written with block and a an explicit loop counter variable if you prefer, like this

for my $word (@array) {
  ++$counts{$word};
}

with exactly the same effect.

Upvotes: 6

Nate
Nate

Reputation: 1921

You can use exists.

http://perldoc.perl.org/functions/exists.html

Given an expression that specifies an element of a hash, returns true if the specified element in the hash has ever been initialized, even if the corresponding value is undefined.

foreach my $fruit (@array) {
    if (exists $hash{$fruit}) {
        $hash{$fruit}++;
    }
}

Upvotes: 1

Related Questions