Counting how many times each distinct word appears in an input In chapter 3 of Koenig's Accelerated C++, he asks the reader to write a program that performs the task described in the title. At this point, he has only introduced the built-in types, io-types, and vectors, so maps are not allowed. I have written a program that does this, but it is lengthy and requires a number of objects that essentially duplicate existing vectors.

Note: the use of while (cin &gt;&gt; input) will not terminate without an explicit end of line character because of the input type, so a better way to handle the input would be nice to have!

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;

using std::cin;
using std::cout;
using std::vector;
using std::string;
using std::endl;

int main ( int argc, char** argv )
{
    string input;
    vector&lt;string&gt; input_arr;

    cout &lt;&lt; "Please enter a sentence or list of words, separated by spaces,"
            "and terminated by an EOL character(^d): ";

    while(cin &gt;&gt; input) {
        input_arr.push_back(input);
    }

    sort(input_arr.begin(), input_arr.end());

    vector&lt;string&gt;::size_type len = input_arr.size();

    uint count = 0;
    vector&lt;uint&gt; count_arr;
    string current_string = input_arr[0];
    vector&lt;string&gt; unique_words;
    unique_words.push_back(input_arr[0]);

    for (vector&lt;string&gt;::size_type i = 0; i &lt; len; i++) {

        count_arr.push_back(0);

        if (current_string != input_arr[i]) {
            count +=1;
            current_string = input_arr[i];
            count_arr.push_back(0);
            unique_words.push_back(input_arr[i]);
        }

        count_arr[count] += 1;
    }

    cout &lt;&lt; endl;

    for (int j = 0; j &lt;= count; j++) {
        cout &lt;&lt; endl &lt;&lt; "The word " &lt;&lt; unique_words[j] &lt;&lt; " occured " &lt;&lt; count_arr[j] &lt;&lt; " times!";
    }

    cout &lt;&lt; endl;

    return 0;
}


In Python, I could achieve this in about 5 lines, for example:

from collections import defaultdict

print("Please enter some text: ")

input_arr = input().split()
d = defaultdict(int)

for k in input_arr:
    d[k] += 1

for key in d.keys():
    print("The word %s occurs %d times!\n" %(key, d[key]))


I am looking for a better way to accomplish this in C++, preferably not using std::maps, since the author probably put this exercise at this stage in the book for a reason. I'm also just learning C++, do I'd appreciate any advice on best practices that I'm violating in this example.
Old book:

The age of the book starts to get noticable. May be before 2011 it would be still very effective, but not that much now. May be not in 5 lines, but it is possible write it pretty shortly and neatly in upcoming C++17

#include &lt;iostream&gt;
#include &lt;iterator&gt;
#include &lt;vector&gt;
#include &lt;map&gt;

int main() {
    std::vector&lt;std::string&gt; all_words{std::istream_iterator&lt;std::string&gt;(std::cin),
                                       std::istream_iterator&lt;std::string&gt;()};
    std::map&lt;std::string, std::size_t&gt; unique_words;
    for (const auto&amp; word: all_words)
    {
        ++unique_words[word];
    }

    for (const auto&amp; [word, count]: unique_words)
    {
        std::cout &lt;&lt; "word " &lt;&lt; word &lt;&lt; " occured " &lt;&lt; count &lt;&lt; " times\n";
    }
}


live demo. Be sure to checkout stdin though, I'm not sure if it is preserved.

Unfortunately there are not many books about recent versions of C++, but knowing the basics should be enough to learn new stuff.

It is also possible to write this without using a map, albeit much more sophisticated:

#include &lt;iostream&gt;
#include &lt;iterator&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;string&gt;

int main()
{
    std::vector&lt;std::string&gt; all_words{std::istream_iterator&lt;std::string&gt;(std::cin),
                                       std::istream_iterator&lt;std::string&gt;()};

    std::sort(all_words.begin(), all_words.end());
    auto first_duplicate_pos = std::unique(all_words.begin(), all_words.end());

    for (auto first = all_words.begin(); first != first_duplicate_pos; ++first)
    {
        auto count = std::count(first_duplicate_pos, all_words.end(), *first);
        ++count; //since occured once in non duplicate part
        std::cout &lt;&lt; "word " &lt;&lt; *first &lt;&lt; " occured " &lt;&lt; count &lt;&lt; " times\n";
    }
}


The idea here is that after using std::unique&lt;&gt;(), the vector is divided into two ranges: one that has only unique elements, and other has duplicates of unique elements, if any. Now, map is not needed, because in fact the first range is map keys with value equal to 1 (remember that unique counts too). Then we just iterate through the other half searching for one of the keys at a time. 

Do note that both versions are invincible to the problem mentioned by @jvb. Thus, it is much better to learn to use standard library first, rather than trying to implement it by hand.

Code Review:

int main ( int argc, char** argv )


No need to have arguments if code is not using it.

sort(input_arr.begin(), input_arr.end());


It would be better to just use std::unique&lt;&gt;() after this and count every occurrence of the elements in range.

Using std::endl should be avoided. It flushes the stream, which is pretty slow. Even Bjarne Stroustrup (original creator of the language) avoids that himself. Instead just use '\n'. std::endl is not only portable way to print a newline, but it also incurs overhead.

for (int j = 0; j &lt;= count; j++)


This lines seems dangerous. I was not able to comprehend the counting logic, but usually when &lt;= count appears, it means getting out of range, which is undefined behavior if it would be the whole vector. I'm pretty sure it should be &lt; count. Anyway, it should've been possible to just use range loop and not worry about the comparison operator.

using std::cin;
using std::cout;
using std::vector;
using std::string;
using std::endl;


This probably didn't buy anything, but instead increased amount of code needed to write. I recommend just sticking with std::. If the namespace is too long, for example std::experimental::filesystem, I recommend doing

namespace exfs = std::experimental::filesystem;


at function scope!

update:

People in the comments mentioned that std::unique&lt;&gt;() is not a partitioning algorithm and the right side of the sequence has unspecified values. It is true. I present another solution, which is standard compliant (I actually went on searching if std::unique&lt;&gt;() always performs swap, but it seems like it is not the case).

#include &lt;algorithm&gt;
#include &lt;iterator&gt;
#include &lt;iostream&gt;
#include &lt;functional&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

int main()
{
    std::vector&lt;std::string&gt; all_words(std::istream_iterator&lt;std::string&gt;(std::cin),
                                       {});
    std::sort(all_words.begin(), all_words.end());

    auto first_occurence = all_words.begin();
    while (first_occurence != all_words.end())
    {
        auto next_word_occurence = std::find_if_not(first_occurence, all_words.end(),
                                                    [&amp;first_occurence](const auto&amp; val)
                                                    {
                                                        return val == *first_occurence;
                                                    }
        );

        std::cout &lt;&lt; "word " &lt;&lt; *first_occurence &lt;&lt; " occured "
                  &lt;&lt; std::distance(first_occurence, next_word_occurence)
                  &lt;&lt; " times\n";
        first_occurence = next_word_occurence;
    }
}


I didn't use std::adjacent_find&lt;&gt;() because it obfuscates the main goal, in my opinion. Using std::find_if_not&lt;&gt;() and its non not version is arguable, but I just went with not version because I usually read on high speeds and sometimes tend to confuse != and ==. Do note that this version is still immune to the point mentioned by @jvb.