Reputation: 37
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
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';
}
}
}
Upvotes: 2