A Simple STL map Example

The initial problem:

Given a text file, count the number of occurrences of each word in the file, then print the all of the words alphabetically with their corresponding count. For example, if a file contained this line:

a line of text that has a word that occurs more than once in the line
it would be presented like this:
2 a
1 has
1 in
2 line
1 more
1 occurs
1 of
1 once
1 text
1 than
2 that
1 the
1 word
Before implementing this algorithm with std::map, how would you implement it without using any STL containers? In other words, using only arrays, linked-lists, or other data structure that you might invent.


A First Attempt

The algorithm goes like this:
  1. Open a file for input
  2. While there are more words
    1. Read a word.
    2. Find the word in the map. (map::find)
    3. If the word is in the map, increment the count by 1.
    4. If the word is not in the map, add it to the map (map::insert) and set its count to 1.
  3. Print out the counts and the words sorted alphabetically (by word, of course).
Sample code:

void CountWords1(void)
{
    // For convenience
  typedef std::map<std::string, int> FreqMap;

  std::string word; // the input word
  FreqMap wf;       // the frequencies of each word

    // Open some text file
  std::ifstream infile("C:\\line.txt");

    // Read all words from the file
  while (infile >> word)
  {
      // See if the key/value pair is already
      // in the map
    FreqMap::iterator it = wf.find(word);

      // If it is present, increment the count (value)
    if (it != wf.end())
      it->second++;  // Same as: (*it).second++
    else
    {
        // Create a new pair with value set to 1
      std::pair<std::string, int> pr(word, 1);
      wf.insert(pr);
    }
  }

    // Print out all of the key/value pairs
  for (FreqMap::iterator it = wf.begin(); it != wf.end(); ++it)
    std::cout << it->second << " " << it->first << std::endl;
}


A Slight Modification

We can take advantage of the subscript operator that is implemented by std::map. This has a couple of very handy features.

Only the loop is modified:

  // Read all words from the file
while (infile >> word)
{
    // See if the key/value pair is already
    // in the map
  FreqMap::iterator it = wf.find(word);

    // If it is present
  if (it != wf.end())
    it->second++;  // increment existing value
  else
    wf[word] = 1;  // else "add" key with value set to 1
}
Given the knowledge of the subscript operator, we can do better at this point. We don't have to call find to locate the item. We can just add it with the subscript operator and it will find it for us:

  // Read all words from the file
while (infile >> word)
{
  int count = wf[word]; // Get current value
  wf[word] = count + 1; //   and update it
}
Of course, we can go further and be more like C++:

  // Read all words from the file and update the count in the map
while (infile >> word)
  ++wf[word];
Ironically, the code to print the contents of the map is more than the code needed to build of the map.

The final (so far) version:

void CountWords4(void)
{
    // For convenience
  typedef std::map<std::string, int> FreqMap;

  std::string word; // the input word
  FreqMap wf;       // the frequencies of each word

    // Open some text file
  std::ifstream infile("C:\\preamble.txt");

    // Read all words from the file and update the count in the map
  while (infile >> word)
    ++wf[word];

    // Print out all of the key/value pairs
  for (FreqMap::iterator it = wf.begin(); it != wf.end(); ++it)
    std::cout << it->second << " " << it->first << std::endl;
}
Given a file containing this text:

When, in the course of human events, it becomes necessary for a people to advance from that 
subordination in which they have hitherto remained, and to assume among the powers of the 
earth, the equal and independent station to which the laws of nature and of nature's god 
entitle them, a decent respect to the opinions of mankind requires that they should declare the 
causes which impel them to the change.
We would get this (formatted with columns for the browser):

1 When,
2 a
1 advance
1 among
3 and
1 assume
1 becomes
1 causes
1 change.
1 course
1 decent
1 declare
1 earth,
1 entitle
1 equal
1 events,
1 for
1 from
1 god
1 have
1 hitherto
1 human
1 impel
2 in
1 independent
1 it
1 laws
1 mankind
1 nature
1 nature's
1 necessary
5 of
1 opinions
1 people
1 powers
1 remained,
1 requires
1 respect
1 should
1 station
1 subordination
2 that
8 the
1 them
1 them,
2 they
5 to
3 which

Future modifications: