Pulis
Pulis

Reputation: 37

Need help combining two STL Maps into one(map<char,string> and map<string,int> into map<char,map<string,int>>)

I'm dealing with a problem in trying to convert my program into using only one map instead of two separate ones.

The goal of the program is to take a text file, store all the words from the file for each letter in a map map<char,string> (a- apples august, b-banana, c-citrus coconut etc) but also keep track of how often each word appears in a text file with a map (apples 2, august 1, banana 2 etc). The last thing is that I need to output the most common words for each letter and the respective number of occurences in another text file.

I have a program which does this but I need to update it so that instead of using two separate maps, it's all combined into one map map<char,map<string,int>> where the char is the letter and the <string,int> pair is the word which starts with this letter and the number of occurrences for this word in the text file. The biggest problem I'm facing is editing the lines that use the find() and insert() functions.

Here is my current code

#include <iostream>
#include <map>
#include <string>
#include <fstream>
using namespace std;
bool onlyLetters(string s) ///function which checks if a string contains only letters of the Latin alphabet
{   bool a= true;
    for(int i=0;i<s.length();i++){
        if(!(s[i]>='a' && s[i]<='z')){
                a=false;
                break;}
        else a=true;
    }
    return a;
}
void stringLowercase(string &s) ///function which converts all letters in a word to lowercase
{
    for(int i=0;i<s.length();i++)s[i]=tolower(s[i]);
}
void insertMostCommon(char a, map<string,int> occurrences, map<string,int> &result) ///function which outputs the pairs (most common word-number of occurences) in the result map
{
    int maximum=-1;
    string maxKey; ///stores the most common word for char a
    for(map<string,int>::iterator it=occurrences.begin();it!=occurrences.end();++it) {
        if(it->second > maximum && it->first[0] == a) {
            maxKey = it->first;
            maximum = it->second;
        }
    }
    result[maxKey] = maximum; ///adds the most common word for char a and how many times it appears in the text file in the result map
}

int main()
{   typedef map<string,int> siMap; ///siMap-stringintMap
    typedef map<char,string> csMap; ///csMap- charstringMap
    ifstream fin;
    ofstream fout;
    fin.open("file.txt", ios::in);
    fout.open("output.txt", ios::out);
    csMap dictionary; ///table which will store pairs letter-word
    siMap occurrences; ///table which will store pairs word-number of occurrences
    siMap result; ///end result table which will store pairs mostCommonWordForLetter-numberOfOccurrences
    string word; ///input word
    while(fin >> word) { ///reads word from file
        stringLowercase(word); ///converts the word to lowercase
        if(onlyLetters(word)){ ///string 'word' only gets input into a table if it only contains letters
                siMap::iterator it=occurrences.find(word); ///checks if the word is already in the table
                if(it!=occurrences.end()) it->second++; ///if it is, increments the count by one
                else {
                    pair<string,int> pr(word,1); ///if it's not found, creates a new pair with value 1
                    occurrences.insert(pr);
                }
                csMap::iterator it2=dictionary.find(word[0]); ///checks if the letter is already in the table
                if(it2!=dictionary.end()) it2->second+=word+" "; ///if it is, adds the word to the list of words starting with this letter
                else {
                    pair<char,string> pr(word[0],word+" "); ///if it's not found, creates a new pair letter-word
                    dictionary.insert(pr);
                }
            }
        }

    for(csMap::iterator it2 = dictionary.begin(); it2!=dictionary.end(); ++it2) {
        cout << it2->first << " " << it2->second << endl; ///prints out the pairs letter-words that start with this letter
    }
    cout << endl;
    for(siMap::iterator it = occurrences.begin(); it!=occurrences.end(); ++it) {
        cout << it->first<< " " << it->second << endl; ///prints out the number of occurrences for each word
    }
    for(char a='a'; a<='z';a++)insertMostCommon(a,occurrences,result); ///inputs the results into the end table for each letter of the alphabet
    auto it=result.begin();
    result.erase(it); ///removes the empty pair ('' -1) at the beginning of the table
    cout << endl;
    for(auto &a: result) {
       cout << a.first << " " << a.second << endl;
       fout << a.first << " " << a.second << endl; ///word-number of occurrences
    }
    fin.close();
    fout.close();
}

Here is the file.txt that I'm using for testing purposes:

citrus apple apple leg window aa asda banana banana citrus leg tree arm leg Apple 12313 aPPle coconut

Here is the output.txt

apple 4
banana 2
citrus 2
leg 3
tree 1
window 1

And here is the console output which also contains the contents of the other two maps:

a apple apple aa asda arm apple apple
b banana banana
c citrus citrus coconut
l leg leg leg
t tree
w window

aa 1
apple 4
arm 1
asda 1
banana 2
citrus 2
coconut 1
leg 3
tree 1
window 1

apple 4
banana 2
citrus 2
leg 3
tree 1
window 1

So, as can be seen, the program performs everything as it's required, however I'm having trouble figuring out an efficient way in how to convert this all into a program that uses only one map map<char,map<string,int>> . I will appreciate any help or insight with this problem, thank you!

Upvotes: 2

Views: 102

Answers (1)

Ted Lyngmo
Ted Lyngmo

Reputation: 117288

I found it a bit hard to read the code so I suggest some simplifications and using standard algorithms where applicable.

Here's an example with comments in the code:

#include <algorithm>
#include <cctype>
#include <cstdint>
#include <iostream>
#include <map>
#include <sstream>

int main() {
    std::istringstream file(
        "citrus apple apple leg window aa asda banana banana "
        "citrus leg tree arm leg Apple 12313 aPPle coconut");  

    std::map<char, std::map<std::string, std::uintmax_t>> res;
    std::string word;

    // populate the container
    while(file >> word) {

        // transform to lowercase
        std::transform(word.begin(), word.end(), word.begin(),
            [](char ch) {
                return std::tolower(ch);
            }
        );

        // add char and word if it only contains letters
        if(
          std::all_of(word.begin(), word.end(), [](char ch){return std::isalpha(ch);})
        ) {
            // res[word[0]]  Creates or returns a reference to an existing
            //               map<string,uintmax_t>.
            // [word]        Creates or uses an existing pair<string,uintmax_t> and
            //               returns a reference to the uintmax_t.
            // ++            Adds 1 to the uintmax_t
            ++res[word[0]][word];
        }
    }

    // print result in C++17 and above:
    /*
    for(const auto& [ch, scmap] : res) {
        std::cout << ch << " (words: " << scmap.size() << "):\n";
        for(const auto& [word, count] : scmap) {
            std::cout << ' ' << count << ' ' << word << '\n';
        }
    }
    */

    // print result in C++11 or C++14:
    for(const auto& ch_scmap : res) {
        char ch = ch_scmap.first;
        auto& scmap = ch_scmap.second;
        std::cout << ch << " (words: " << scmap.size() << "):\n";
        for(const auto& word_count : scmap) {
            const auto& word = word_count.first;
            auto& count = word_count.second;
            std::cout << ' ' << count << ' ' << word << '\n';
        }
    }
}

Demo

Upvotes: 2

Related Questions