Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

Exercise: Letter Frequency

Exercise 16 Easy

Prerequisites for the exercise

  1. JavaScript Strings — Unicode
  2. JavaScript Strings — Basics
  3. All previous chapters

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:

LetterCountLetterCountLetterCountLetterCount
a3b0c0d0
e2f0g1h3
i6j1k1l0
m1n2o2p0
q0r1s5t4
u1v0w3x0
y0z1

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:

a: count b: count c: count d: count e: count f: count g: count h: count i: count j: count k: count l: count m: count n: count o: count p: count q: count r: count s: count t: count u: count v: count w: count x: count y: count z: count

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 a for) 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.

View Solution

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 the y 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 be 88.
  • 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 index 23 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 0s 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 it output.
  • Iterate over letterCounts using for (as instructed in the exercise) with an increment of 4 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.