[MUSIC PLAYING]
In this video, we're going to focus
on how to use hashes in Perl.
Hashes also are known as associative arrays in Perl,
and we'll see why in a little bit.
First, we're going to go through a bit of the theory
of why hashes are useful.
Then we're actually going to write
some Perl code to use them.
It would be really helpful if you understood
how arrays and strings work in Perl
and also have a good understanding of basic syntax
so that you can follow along with the code later.
But this is not necessary for understanding why they're
useful and how they work.
So if you don't, that's OK, You can revise that stuff later,
but it would be helpful to know.
All right, let's get started.
So we're going to go through an example here of, let's say,
a piece of code I want to write where
I want to count the number of occurrences of the letters
'a', 'b', and 'c' in the string 'abracadabra'.
Now let's imagine I only know how
to use a normal array in Perl, in which case
I'm just going to store letter frequencies
at particular indexes.
For example, I might store
all the occurrences of the letter 'a' in the index 0,
all the occurrences of the letter 'b' in the index 1,
and all the occurrences of the letter 'c' in the index 2.
So somehow we've got to convert our letter to that index,
and then we'll store it in an array.
For example, I might have an array in Perl
I've called '@count', in which case I would index it like this
using our index.
Pretty straightforward.
So if I want to convert a character that's inside a Perl
string into an index, we're going
to have to do some funky arithmetic.
So just like how you do it in C you
might just want to immediately subtract the characters.
So let's say I have some character.
You might just want to subtract 'a'
to get its relative position to lowercase a.
So if this here was 'a', I'd get 0.
I'd get 0.
If it was 'b', I'd get 1.
And if it was 'c', I would get 2.
We can't do direct character arithmetic like this in Perl.
We actually have to use a separate function called
'ord' which takes in a character and spits out a number.
So we'd have to do 'ord' of our character, whatever that is,
and subtract 'ord' of the character 'a'.
And this would give us a similar result
to what you'd expect if you did character arithmetic in C.
So what we would do then is once we
have an index we'd go through.
We'd say, oh, we've got the character 'a'.
We'd convert that to the index 0,
and we'd increment our value from 0 to 1.
We have the character 'b'.
We'd convert that to the index 1.
We'd increment our value here.
We would ignore 'r' because it's not equal to 0, 1, or 2.
We find another 'a' and so on.
And we can solve our problem this way.
But this is kind of clunky.
Is there a nicer way that we can do this?
We don't want to have to do all this annoying, weird arithmetic here.
What if we could just store the values
and, instead of using an index, what if we could just
use the character itself?
What if we could use the string instead of an index?
And this is what hashes are built for.
Instead of being forced to use an index, which is a number--
for example, 0, 1, or 2--
you can use other types.
We call them keys instead of indexes,
and they don't have to be numbers.
So we could actually have, instead of
0, 1, or 2, we could use strings as our keys.
So instead of storing or tallying
the values using 0, 1, and 2 as our indexes,
we can just use the strings themselves
in order to store the values.
And this is much more natural because I
could see the character 'c', just use the key in my hash,
add the value, none of this weird arithmetic over here.
Oh, look, there's an 'a'.
Store it where 'a' is.
Increment that.
Look, there's another 'a'.
There's a 'b'.
And 'r' we ignore, and then finally another 'a'.
And that's a much more natural way of doing it.
Now just like arrays, we don't actually
have to initialise them.
The values will also start at 0, but we
have to specify what their keys are
because they're not immediately values
like 0, 1, and 2 as indexes.
We index them exactly the same way.
So if we had a hash, which we initialise with a percent sign.
So let's call it '%hash', very creative name.
We also get the value using a dollar sign,
whatever the name of the hash is.
And instead of square brackets, we're
going to use curly brackets.
Instead of putting an index here,
we put whatever our key is.
For example, you might put our key 'a'.
And then we can use it normally, just
like we were using an array.
So now we're going to write some Perl code to actually put this
into practise, showing you the version with arrays
and the much nicer version using hashes.
So here we've got the beginnings of our program.
It's the super-messy-looking implementation
because we're assuming we only know how to use arrays.
So we're having to do this weird conversion between a character
to an index.
So what we've got here in our code,
we've got an initialised array, which
we don't have to do because they're all zeroes,
but I just put it there anyway.
We've got this foreach loop which
'split's the string 'abracadabra' and goes through it
character by character.
If the character matches either 'a', 'b', or 'c', then what we do
is we're going to convert it to an index.
So just like we saw before we're going
to use 'ord' which takes in a string
that looks like a character and converts it to its ASCII value.
We're going to subtract it from lowercase 'a'
so that we can get the difference.
So as we saw before, 'a' minus 'a' would be 0.
'b' minus 'a' would be 1, and 'c' minus 'a' would be 2.
So that will convert it our index for us.
And then we index our array called '@count'
and we increment it.
Also, I've got some code at the bottom
that's going to print out each of the values.
So we're going to go through the array
through each of the indexes from 0 to the size of the array.
We're going to have to work backwards
to get the character, which means
we're going to have to use 'ord' again
to add the value of the character a to whatever
our index is and then use this function to convert it back
into a character.
And then we have this string down at the bottom
which prints out the character and then the count,
indexing the array at the index i.
So now you understand what the program's doing,
I'm going to run it.
So program is called 'char_count.pl'.
When I hit Enter, we see that we've
got the character 'a' appearing five times.
'b' appears two times, and 'c' appears once.
So our code is working.
That's good.
But let's actually make it nicer and change it into a hash.
So what we're going to do first, I'm
going to keep using the name count,
but I'm going to change it into a hash.
So what I'm going to do, if I wanted to initialise a hash,
instead of using the '@', I'm
going to change it to a '%'.
That tells Perl that it's a hash or an associative array instead
of a normal array.
And unlike a normal array, which has the indexes assumed
for you-- for example, with the original count already
you've got 0, 1, and 2 as the indexes--
we actually have to tell Perl what the keys are
when we initialise it.
So how we do that is for each of the keys
we have to assign it a value.
So we're going to type out the key.
In this case, let's do the string 'a' as one of the keys.
I use equals and then the greater than sign
to make an arrow, and I point to the value, in which case
this is 0.
I'm going to do the same with the string 'b'.
Assign that to the value 0.
And I'm going to do the same thing with the string 'c'
and assign that to the value 0.
So this would be how we'd initialise the hash.
Now because all the values are 0, just like with arrays
you don't actually have to initialise it.
But this is helpful if you wanted
to start with other values-- for example,
you wanted to set different values for each of these.
This would be how you'd initialise.
OK, now let's go in and change the rest of our code.
So we've got in this second for loop
we're doing this weird conversion to an index.
With a hash we don't actually need to do that.
So instead of this line where we convert it to an index,
we don't actually have to do that.
We can use the character itself as the key.
How we do that is instead of using square brackets, which
is how we get the value of an array using an index,
we're actually going to use curly brackets here
and we're going to use the character instead.
Much simpler.
All right, now let's go down to the bottom loop as well.
Now what we're going to do,
we're going to access the values of the hash.
Now we do that using a foreach loop just like we
would with an array, but it doesn't just have one value.
It has two things:
it has a key and then it has a value.
So we need to somehow use both.
So what we're actually going to do
is we're going to, instead of for each index '$i',
we're going to do foreach $key.
And we've got to somehow get the keys of our hash.
So I'm going to write the name of the hash which is '%count'.
And Perl has this really useful function
called 'keys' which gets the keys of a hash
and returns them as a list.
In this case, 'keys %count' would contain the string values
of 'a', 'b', and 'c'.
We can use those to access the values just like we would
as if it was an index.
So now we've got the key.
We don't have to do this weird mess
to get the character anymore.
Instead, the character is just equal to the key.
And just like before, instead of indexing using square brackets,
we're going to use our curly brackets,
and we'll use the key instead of the index.
Now I'm going to save our program and we're going
to run it, and you should see we get exactly the same output,
except this time it's much nicer.
So look, we've got exactly the same thing except now we're
using a hash instead of an array.
It's much nicer to index using the string
instead of having to do weird, strange arithmetic.
So I'll run it again.
Oh, hmm, the values are right, but we
seem to have got a bit of a problem here.
Some of the orders of the keys aren't
exactly in the right order.
We've got the right keys and values,
like 'c' is always equal to 1,
the value of the string 'b' is always
equal to 2 and 'a' is always equal to 5,
but they're all coming out in different orders.
Why is that?
Well, you can see how it would make sense for an array
that the index 0 would come before 1.
1 would come before 2, and 2 would come before 3, et cetera.
When you're using indexes in an array
they have an inherent order.
But when you're using keys in a hash, there's no inbuilt order.
Perl doesn't know what comes before what.
So it essentially just gives you the keys in any random order.
We don't want that.
We want the keys to be sorted.
So if we were to get the keys in sorted order,
we use the function 'sort'.
Since 'keys %count' gives us a list or an array of the keys,
we know how to sort arrays.
You just use the 'sort' function in front of it.
So this will sort the keys so that every time we run
the program they're in the same order.
And now every time I run it, we get them
in the order of the keys.
All right, so what if we want to sort on the values instead
of the keys?
Well, if we want to get a list of the sorted values
we can do it this way.
So I can have my '@values' array.
I use this super-helpful function called 'values'.
I pass it the hash called '%count', and just
like before, I can sort them.
So this will sort my values for me.
And then I can print them out like this.
This will give us our values in order of ascending order.
So when I run this, you get 1, then 2, then 5,
which is what we want.
But just like with arrays, if you have the values,
it's really hard to go back to the key in the same way
that you, with arrays, can't go back to the index
when you have the value.
So we can get the values in sorted order,
but what if we want to get the keys and the values
but sort them based on the value instead?
Well, what we can do is we can give it a block and say,
well, we don't actually want to sort on the values themselves.
What we want to sort, the keys, but we
don't want to sort on the keys.
We want to sort on the values the keys point to in the hash.
So in our block what we're going to do,
instead of sorting '$a' and '$b' directly, in which case
this would be sort comparing the keys,
we want to compare the values of the keys in the hash.
So I'm going to access the hash like this,
using our curly brackets again to get the value,
and I'm going to compare the values instead of the keys.
So what this will do is 'keys %count' will give us a list
of all the keys.
And then if we sort using this block here,
this will compare the value of the key 'a' in the hash
and the value of the key 'b' in the hash.
It will compare them and sort those,
but it will return a list of the sorted keys, which
is what we want-- so '@keys' here.
Let's run our program again.
Now this is interesting.
We get our keys in order 'c', 'b', 'a', which makes sense.
Those are the corresponding keys for the order
of the values 1, 2, and 5.
So now, instead of just using the sorted keys here,
we can use our '@keys' list like this.
And if we run our program, now we'll get the keys
and the values in the order that we want.
Now what we can do is what if we wanted to add
other values to our hash?
What if we wanted to add a longer string?
Well, you can totally do that.
So if I decided that I wanted to add something later
on in my program to my hash, I can just add the key like this.
So maybe I might use the string 'abba'.
And I'll give it a value.
Let's say 3.
And now if we run this program again,
what we should get is we should get the values in ascending
order and we should get the keys in that order as well.
So when I run this, yeah, we get 'c', 'b', 'abba', and 'a'.
And then we see the same thing here.
We get them in the order of values 1, 2, 3, and 5.
If I want to switch back to see what it looks like if I just
sort on the keys, I can sort the keys of our hash.
And you should see that now we're
working in terms of the alphabetical order
of the keys-- so 'a', then 'abba', then 'b', then 'c'.
All right, let's go back to using our sorted keys.
So this is the end to our introduction on hashes.
As a final note, which will be helpful for you guys to know,
there's a similar structure that exists in Python that also
associates keys and values.
In Python it's called a dictionary.
The syntax will be a little different.
Like in Python, for example, you do have to initialise things.
And, obviously, Python's syntax is slightly different.
But essentially if you want to associate keys and values
in Python, you'd use a dictionary whereas in Perl you
would use a hash.
So it's helpful to keep that in mind.
And now you guys know how you can use hashes in Perl.
[MUSIC PLAYING]