3

How can I print the contents of a nested map? I am counting the number of times a word appears in a file, reporting it by line number and number of times per line. The words, lines, and occurrences per line are being stored in the following container:

map<string, map<int, int>> tokens;

However, I'm not sure on the syntax. I am printing the outer map that lists all of the words using the following code, but can't figure out how to print the inner values (the line number and number of times the word appears on each line) as well. I assume I can just include it inline in the for loop, but I can't figure out how:

for (map <string, map<int, int>>::iterator it = tokens.begin(); it != tokens.end(); ++it){
    cout << it->first << " : " << /* assume I can include another statement here to print the values? */ endl;
}

I am trying to get an output similar to this:

(word : line:occurrences, line:occurrences, ...)

about : 16:1, 29:1, 166:1, 190:1, 191:1
above : 137:1
accompanied : 6:1
across : 26:1
admit : 20:1
advancing : 170:1
.
.
.
honk
  • 9,137
  • 11
  • 75
  • 83
CGutz
  • 537
  • 2
  • 7
  • 20

3 Answers3

5

It is actually pretty simple.

You just get the internal map with it->second, and you iterate through that the same way.

Thereby, you would write something like this:

for (map <string, map<int, int>>::iterator it = tokens.begin(); it != tokens.end(); ++it){
    cout << it->first << " : ";
    map<int, int> &internal_map = it->second;
    for (map<int, int>::iterator it2 = internal_map.begin(); it2 != internal_map.end(); ++it2){
        if (it2 != internal_map.begin())
            cout << ",";
        cout << it2->first << ":" << it2->second;
    }
    cout << endl;
}

You could write something like this if you have C++11 support:

for (auto it : tokens) {
    cout << it->first << " : ";
    map<int, int> &internal_map = it->second;
    for (auto it2: internal_map) {
        if (it2 != internal_map.begin())
            cout << ",";
        cout << it2->first << ":" << it2->second;
    }
    cout << endl;
}
László Papp
  • 51,870
  • 39
  • 111
  • 135
  • 1
    This would be so much more elegant in C++11 :) – M.M Apr 06 '14 at 02:46
  • 1
    @MattMcNabb: C++11 is not available everywhere, but the solution is provided either way. – László Papp Apr 06 '14 at 02:46
  • Definitely like the auto version, but nice to see the type, as well, since I don't understand iterators very well and am trying to get better at them. When trying to do this, I get an error on the stream output operator before it2->second saying "no operator << matches these operands". Any idea why that would be? – CGutz Apr 06 '14 at 02:52
  • 1
    I made a mistake of "->" instead of "." for the auto version, though. I will fix that a bit later after getting some more feedback because it was already edited several times now. There is also a space missing after the comma. :) – László Papp Apr 06 '14 at 02:52
  • Just trying to output using your first method without auto. The line: cout << it2->first << ":" << it2->second; gives me an error. – CGutz Apr 06 '14 at 02:55
  • 1
    I cannot possibly imagine that happening without a typo, etc. – László Papp Apr 06 '14 at 02:58
  • Ah, doh. I had it->second instead of it2->second – CGutz Apr 06 '14 at 03:03
1

C++17

Since C++17 you can use range-based for loops together with structured bindings for iterating over maps. This way, the readability of lpapp's C++11 solution can be further improved as follows:

for (auto const &[k1, v1] : tokens) {
    std::cout << k1 << " : ";
    for (auto const &[k2, v2] : v1) {
        if (&k2 != &v1.begin()->first)
            std::cout << ", ";
        std::cout << k2 << ":" << v2;
    }
    std::cout << std::endl;
}

Note: I admit, the check for printing a comma is a bit messy. If you are looking for a nicer solution, you might want to take a look at this Q&A.

Code on Coliru

honk
  • 9,137
  • 11
  • 75
  • 83
0

Since we are only printing the elements, not modifying it, I would make more use of const, (a) const reference and (b) const iterators.

Also, for pre-C++11, it helps to define typedef's for complex types.

#include <string>
#include <map>
#include <iostream>
using namespace std;

typedef map<int, int> InnerMap;
typedef map<string, InnerMap> OuterMap;

void printClassic( OuterMap const & tokens ) {

    for( OuterMap::const_iterator cit = tokens.begin();
             cit != tokens.end(); ++cit ) {
        cout << cit->first << " : ";
        InnerMap const & imap = cit->second;
        for( InnerMap::const_iterator cit2 = imap.begin(); 
                 cit2 != imap.end(); ++cit2 ) {
        cout << cit2->first << ":" << cit2->second << ",";
        }
        cout << endl;
    }
}

void printCpp11( OuterMap const & tokens ) {
    for( auto const & cit : tokens ) {
        cout << cit.first << " : ";
        auto const & imap = cit.second;
        for( auto const & cit2 : imap ) {
            cout << cit2.first << ":" << cit2.second << ",";
        }
        cout << endl;
    }
}
Arun
  • 19,750
  • 10
  • 51
  • 60