#!/usr/bin/perl -w
# To the student:
#
# This tutorial has been written given the assumption that you
# are familiar with what a hash is and what some of the hash
# oriented functions are. It is NOT written to teach you how
# to use a hash in it's most basic form! If you need help with
# that, refer to Perl's documentation or your instructor's 
# notes.
#
# Every attempt has been made to ensure that the information
# presented in this code example is correct and syntactically
# valid. You can yank and paste this code into your text editor
# and run it directly on your own machine. Please report any 
# errors to kaiw@voyager.deanza.edu
#
# ~Kai
#
################# THREE BASIC USES OF HASHES ####################
#
# First, let's review what a hash actually is. Functionally, 
# it is equivalent to an orderless array. Data in a hash can be
# accessed when provided with a particular index. This index is 
# called a KEY. Each key is associated with a particular value,
# setting up what is called a key/value pair.
#
# NOTE: A key cannot have more than one value, nor can the same
# 	key modify two values at the same time!
#
# Just like a phone book has a name which then corresponds to
# a phone number, a hash has a key associated to a value. Now
# that we have the basic uses of hashes down, lets see when
# you as the Perl programmer would want to use one:


############ SITUATION 1: TEXT INDEXING ###########
# 
# Let's say we want to store some bank sums from different people
# into some sort of a usable structure. We could set up two
# parallel lists, one with their names, the second with their 
# corresponding bank balances in the matching index. So
# Mr. Andresen has a balance of $10,000.00 and 
# Ms. Skougaard has a paltry balance of $20.00.

my ( @names, @balances );

@names    = ( "Andresen", "Agarwal", "Nguyen", "Skougaard" );
@balances = ( 10000.00  , 15000.00 , 8000.00 , 20.00       );

# Print the balances
print "Current Balances (from lists):\n";
foreach $i ( 0..@names-1 ) ## Note: without the random -1, we would
{			   ##	    run off by one!
	print "$names[$i]: $balances[$i]\n";
}

# Increase Mrs. Skougaard's balance by $10,000
# First locate her index
$i = 0; # Reset i's value to zero.
foreach $name ( @names )
{
	last if $name =~ /Skougaard/; ## Yuck. Seriously don't do this.
	$i++;
}
$balances[$i] += 10000.00; # Now update her balance

# We see with an arrangement like this the data can certainly be
# represented in memory, but not very effectively. To print the 
# values, you would have to index through each array, which can
# be quite tedious. In addition, both lists would have to be 
# maintained in parallel! This introduces the possibilities for
# off-by-one errors, not to mention it makes NO use of Perl's 
# basic constructs like standard list traversal.
#
### BRING IN THE HASH ###
#
# Instead of the awful (but functional example above, let's try
# this intead:

my %bankBalances;
		## "key" => "value"
%balances = ( 	"Andresen" => 10000.00,
		"Agarwal"  => 15000.00,
		"Nguyen"   =>  8000.00,
		"Skougaard"=>    20.00 );

# Print the balances
print "\nCurrent Balances (from hash):\n";
foreach $name ( sort keys %balances )
{
	print "$name: $balances{$name}\n";
}

# Increase Mrs. Skougaard's balance by $10,000
$balances{ "Skougaard" } += 10000.00; # That's it!

# Look at how much cleaner and easier this is to manage! We
# can easily print the hash and access the hash data! The 
# keys function gets the "keys" to the values, and sort 
# simply puts those keys in a default order.
#
# One thing to note: You can NOT perform sequential data 
# processing with a hash, as a hash is inherently unordered!
# So when you're using the keys function, unless they're
# sorted, the hash keys will come in RANDOM ORDER! This also
# means that you cannot use list oriented functions directly
# on the hash itself. However, there are still nifty things
# to do with hashes and list oriented functions. More on
# that in a different tutorial.


############ SITUATION 2: COUNTING ###########
#
# Perhaps we have more data we need to process for these
# fine people. Let us imagine that they are with 
# Bank of America and BofA leverages a 20 cent charge for
# each time they use their debit card. We'll say that each
# time they charge onto debit, their name gets pushed onto
# a list, and we want to count how many times one person
# charged.

@charges = ( "Andresen","Agarwal","Agarwal","Agarwal","Andresen","Agarwal" );

# I don't even want to think of how to count this with a 
# list. In fact, it's a waste of time for you to even think
# about how to do it with a list. It, again, would require 
# a parallel list mess and would suck down compute time. 
# Lets use a hash instead!
my %chargeCounter;

foreach $person ( @charges )
{
	$chargeCounter{$person}++;
}

print "\nCharge count:\n";
foreach $person ( sort keys %chargeCounter )
{
	print "$person charged $chargeCounter{$person} times.\n";
}

# As we can see, this makes use of the basic Perl rule that
# the first time you use a value it is auto-initialised to
# undefined (zero in a numeric context)! This quickly counts
# the names in the array!


############ SITUATION 3: DUPLICATE CHECKING AND FILTERING ############
# 
# For this third and final task we are going to weed duplicates out of 
# an array. There can be many situations where you need to do this. 
# This is really just a logical extension from above. So to extend off
# the situational example above, we'll say there's a charge list, but
# now we're tasked with charging each person who has used their debit
# card ONE OR MORE TIMES the standard Bank of America fee of $5.00. We
# are not evil so therefore don't want to charge them $5.00 for each
# use DO WE? 
#
# No, so let us filter out the duplicates from the same list:

@charges = ( "Andresen","Agarwal","Agarwal","Agarwal","Andresen","Agarwal" );

my %debitUsed;

foreach $person ( @charges )
{
	$debitUsed{$person} = 1; ## NOTE: this is almost identical to above!
				 ## In fact, you'll see soon that we can really
				 ## use the same hash from above for duplicate
				 ## checking.
}

@chargeThesePeople = keys %debitUsed;

print "\nCharge these people \$5.00: @chargeThesePeople\n";

# Notice, if we want to, we can easily use this hash in a boolean/integer
# context. Any value that is not undefined is true, so it makes it simple
# to do these if tests:

if ( $debitUsed{"Andresen"} ) { print "Andresen used his debit card.\n" }
else { print "Andresen did NOT use his debit card.\n" }

if ( $debitUsed{"Skougaard"} ) { print "Skougaard used her debit card.\n" }
else { print "Skougaard did NOT use her debit card.\n" }


##################### IN CLOSING ##########################
#
# As you can see, hashes are a very powerful part of Perl. This
# is again only some of the BASIC usages of hashing. There are 
# many more advanced techniques that can be employed. I invite
# you all to share with me any other ways you might have 
# used a Perl hash.