Objective
Create a function to determine the frequency of letters of the English alphabet in a given string.
Description
Consider the following piece of text:
This is just amazing to know. I wish I was there.
Ignoring the character casing here, we see that the frequency of every single letter of the English alphabet (in lower case) is as follows:
Letter | Count | Letter | Count | Letter | Count | Letter | Count |
---|---|---|---|---|---|---|---|
a | 3 | b | 0 | c | 0 | d | 0 |
e | 2 | f | 0 | g | 1 | h | 3 |
i | 6 | j | 1 | k | 1 | l | 0 |
m | 1 | n | 2 | o | 2 | p | 0 |
q | 0 | r | 1 | s | 5 | t | 4 |
u | 1 | v | 0 | w | 3 | x | 0 |
y | 0 | z | 1 |
You can even confirm this table by counting the letters manually.
In this exercise, you have to create a function logLetterFrequency()
to output such a piece of information about a given string.
The format of the output, which must be made to the console, shall be as follows:
Each line must show the count of at most four characters, with each character's count represented in the form character: count
, separated from another such statement by four spaces.
Here are a couple of things to keep in mind while coding the solution
- You MUST use a
for
loop to make the output. - You MUST NOT use any nested
for
loops (i.e.for
within afor
) in any part of the code. - At the end of a line in the output produced by the code, there must be no spaces. The spaces must be only between the statements describing the counts of given characters.
Hints
Hint 1
Use an array to keep track of each letter's count.
Hint 2
Convert the given string to lowercase, as the casing should be ignored.
Hint 3
Convert each letter to its corresponding character code to determine the index of the element to increment in the array holding the letter frequency of the string.
New file
Inside the directory you created for this course on JavaScript, create a new folder called Exercise-16-Letter-Frequency and put the .html solution files for this exercise within it.
Solution
This exercise is fairly simple to solve. We just ought to iterate over the given string and determine the count of each letter therein.
So first thing's first, let's decide on what should we use to store the count of all the characters. One option is to use an object with the character as keys.
An example is shown below:
{a: 0, b: 0, c: 0, d: 0, e: 0, f: 0, g: 0, ...}
Each time a character is encountered in the string provided to logLetterFrequency()
, it's checked whether it's a key of the object above, and if it is, 1
is added to the value of the key.
For instance, let's suppose that the character encountered is y
.
- Clearly, it's a key of the object shown above, and so this first check gets cleared.
- Thereafter,
1
is added to the value of they
property of the object.
Not that difficult to get the hang of, right?
Another option is to use an array. Since we are just interested in the characters a
- z
, which form a sequence, we can easily model this in the form of an array.
An example follows:
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
This array consists of a total of 26 elements, each of which is 0
and simply represents the count of a particular character. The first element represents the count of a
, the second one represents the count of b
, and so on and so forth.
When reading the string provided to logLetterFrequency()
, if a character is in the range a
- z
, then its corresponding code is determined by using charCodeAt()
, then its offset is computed in the array by subtracting the code of a
from the code of the character, and then finally 1
added to the corresponding element.
For instance, let's suppose that the character encountered is y
.
- Clearly, it's within the range
a
-z
, and so this first check gets cleared. - Moving on, the character code of
y
is found out to be88
. - This coincides with the offset
23
in the array above (because 88 - 65 = 23, where 65 is the code of the first element). - In the end,
1
is added to the element at the index23
in the array above.
Easy?
Now we'd go with the latter approach here, since it's a little bit easier to iterate over an array than to iterate over an object (at least based on what we have covered thus far in this course uptil this point).
Plus, it's also easier to type 0
26 times, for the array, than to type a
and 0
, then b
and 0
, then c
, and 0
,..., you get the idea, right?
Alright, so now it's time to start coding.
First, let's set up an array letterCounts
as a collection of 26 0
s to represents the count of each letter of the English alphabet in the string str
:
function logLetterFrequency(str) {
var letterCounts = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
}
Next up, let's convert str
into lowercase characters, because we need to ignore the casing:
function logLetterFrequency(str) {
var letterCounts = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
str = str.toLowerCase();
}
After this, let's iterate over str
and perform the procedure mentioned above to determine the count of the letters therein:
function logLetterFrequency(str) {
const CODE_LOWERCASE_A = 'a'.charCodeAt();
var letterCounts = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
str = str.toLowerCase();
for (var i = 0, len = str.length; i < len; i++) {
// If the character is a letter in the range a-z,
// increment its count in the letterCounts array.
if ('a' <= str[i] && str[i] <= 'z') {
letterCounts[str[i].charCodeAt() - CODE_LOWERCASE_A]++;
}
}
}
Note that we define a constant CODE_LOWERCASE_A
that holds the character code of a
. This is needed when we want to compute the corresponding offset of str[i]
(in the the for
loop) in letterCounts
.
The final step now is to just output the information stored in letterCounts
.
Following from what's mentioned in the description above — to use a for
loop to do this; to not use a nested set of for
loops; and also to not use console.log()
more than once — we'll do this as follows:
- Define an accumulator string variable meant to store the text that we'll output in the end of
logLetterFrequency()
. Let's call itoutput
. - Iterate over
letterCounts
usingfor
(as instructed in the exercise) with an increment of4
to the loop's counter variable after each iteration. - At each iteration, create a string containing information about the next 4 characters in line, and then concatenate this string to
output
. - Once the loop completes, log
output
.
Simple?
Here's the code implementing this idea:
function logLetterFrequency(str) {
const CODE_LOWERCASE_A = 'a'.charCodeAt();
var letterCounts = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
str = str.toLowerCase();
for (var i = 0, len = str.length; i < len; i++) {
// If the character is a letter in the range a-z,
// increment its count in the letterCounts array.
if ('a' <= str[i] && str[i] <= 'z') {
letterCounts[str[i].charCodeAt() - CODE_LOWERCASE_A]++;
}
}
var output = '';
for (i = 0, len = letterCounts.length - 4; i < len; i += 4) {
output += `${String.fromCharCode(i + CODE_LOWERCASE_A)}: ${letterCounts[i]} `
+ `${String.fromCharCode(i + 1 + CODE_LOWERCASE_A)}: ${letterCounts[i + 1]} `
+ `${String.fromCharCode(i + 2 + CODE_LOWERCASE_A)}: ${letterCounts[i + 2]} `
+ `${String.fromCharCode(i + 3 + CODE_LOWERCASE_A)}: ${letterCounts[i + 3]}\n`;
}
}
All's great so far, with just one thing left.
Since there are a total of 26 characters, this approach would leave the last two characters (y
and z
) unattended. However, we could easily address them manually, as shown below:
function logLetterFrequency(str) {
const CODE_LOWERCASE_A = 'a'.charCodeAt();
var letterCounts = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
str = str.toLowerCase();
for (var i = 0, len = str.length; i < len; i++) {
// If the character is a letter in the range a-z,
// increment its count in the letterCounts array.
if ('a' <= str[i] && str[i] <= 'z') {
letterCounts[str[i].charCodeAt() - CODE_LOWERCASE_A]++;
}
}
var output = '';
for (i = 0, len = letterCounts.length - 4; i < len; i += 4) {
output += `${String.fromCharCode(i + CODE_LOWERCASE_A)}: ${letterCounts[i]} `
+ `${String.fromCharCode(i + 1 + CODE_LOWERCASE_A)}: ${letterCounts[i + 1]} `
+ `${String.fromCharCode(i + 2 + CODE_LOWERCASE_A)}: ${letterCounts[i + 2]} `
+ `${String.fromCharCode(i + 3 + CODE_LOWERCASE_A)}: ${letterCounts[i + 3]}\n`;
}
// Manually deal with the letters y and z.
output += `y: ${letterCounts[24]} `
+ `z: ${letterCounts[25]}`;
console.log(output);
}
And this completes this exercise.