I am reading from a file and take the words as tokens with strtok. I am trying to store the words in a map structure. I don't really know how to insert the tokens in the map.
My code so far:
#include <iostream>
#include <string.h>
#include <fstream>
#include <map>
using namespace std;
//std::map <string, int> grade_list;
int main()
{
std::map <string, int> grade_list;
char text[100];
int nr=0, i=1;
char *ptr;
ifstream myfile("ana.txt");
if(!myfile.is_open())
cout << "Could not open file" << endl;
else
{
myfile.get(text, 100);
ptr = strtok(text, " ,.-?!");
while(ptr != NULL)
{
nr++;
cout << ptr << endl;
ptr = strtok(NULL, " ,.-?!");
grade_list.insert(ptr);
i++;
}
}
cout << "\nAveti " << nr << " cuvinte." << endl;
return 0;
}
std::map is an associative container, provides Key -> Value relationship. In your case it is std::string -> int. So, you should specify Value while inserting too:
grade_list[ptr] = nr;
Also, instead of char array and using strtok I suggest use std::string and boost::algorithm::split, or boost::tokenizer.
I want to see for each word in the file how manny times it appears in the text.
So, you have to change Value type in map to std::size_t(since you din't need to negative values):
std::map <string, std::size_t> grade_list;
And just write:
++grade_list[ptr];
You should probably look at the std::map::insert definition, the value_type parameter is a std::pair< std::string, int > so you should probably write the insert statement as:
grade_list.insert(std::pair< std::string, int >(std::string(ptr), 1));
This will add an entry into the map with the key "token" and the value 1.
What you probably want is more like add an entry if it does not exist or increment the value :
this can be achieved by writing something like
if (grade_list.find(ptr) == grade_list.end())
{
// insert new entry
grade_list.insert(std::pair< std::string, int >(std::string(ptr), 1)); // can be written as grade_list[ptr] = 1;
}
else
{
// increment token
grade_list[ptr] += 1; // can be written as grade_list[ptr]++;
}
Related
I have this following code where there is a vector of strings. Each string is an integer. I want to sort this in a descending order.
The regular sort function did not solve my problem.
Can someone point out how to do this? I want the output as 345366,38239,029323. I want the leading zero in 029323 as well.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
int main() {
vector<string> v = {"345366", "029323", "38239"};
vector<int> temp(v.size());
for (int idx = 0; idx < v.size(); idx++)
temp[idx] = stoi(v[idx]);
sort(temp.begin(), temp.end()));
cout<<temp[0]<<" "<<temp[1]<<" "<<temp[2];
return 0;
}
You can use a comparator function like this:
vector<string> v = {"345366", "029323", "38239"};
std::sort(v.begin(), v.end(), [](const std::string &s1, const std::string &s2) -> bool {
return std::stoi(s1) > std::stoi(s2);
});
for(auto i : v)
cout << i << endl;
Check this std::stoi() reference.
Edit: From the comments, it seems std::stoi() is much better than std::atoi(). For converting C++ strings, use std::stoi(). For C strings, std::atoi() will silently fail without generating any error if the string is not convertible to int, whereas std::stoi() will generate an exception, thus is a safer choice as well.
cout << std::atoi("abc") << endl; // runs smoothly
cout << std::stoi("abc") << endl; // creates an 'uncaught exception of type std::invalid_argument: stoi'
However, the results will be the same in this case (will extract the prefix integer part and exit, in case of std::stoi(), if the string doesn't begin with integers, it will create an exception):
cout << std::atoi("999abc12") << endl; // prints 999
cout << std::stoi("999abc12") << endl; // prints 999
cout << std::stoi("abcdef12") << endl; // generates exception
Also see this answer.
How to get key using value which is vector of string and vice versa. Below is my code.
#include<iostream>
#include<map>
#include<string>
#include <unordered_map>
#include <vector>
using namespace std;
int main()
{
std::unordered_map<std::string, std::vector<std::string>> Mymap;
Mymap["unique1"] = {"hello", "world"};
Mymap["unique2"] = {"goodbye", "goodmorning", "world"};
Mymap["unique3"] = {"sun", "mon", "tue"};
for(auto && pair : Mymap) {
for(auto && value : pair.second) {
std::cout << pair.first<<" " << value<<"\n";
if(value == "goodmorning") // how get key i.e unique2 ?
}}
}
case 1: When value is input. key is output.
Input : goodmorning
output : unique2
case 2: When key is input value is output.
Input : unique3
output: sun ,mon ,tue
Note : No boost library available.
For case 1, a combination of find_if and any_of will do the job.
For case 2, you can simply use the find method of unordered_map.
#include<iostream>
#include<map>
#include<string>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
unordered_map<string, vector<string>> Mymap;
Mymap["unique1"] = { "hello", "world" };
Mymap["unique2"] = { "goodbye", "goodmorning", "world" };
Mymap["unique3"] = { "sun", "mon", "tue" };
// Case 1
string test_value = "goodmorning";
auto iter1 = find_if(Mymap.begin(), Mymap.end(),
[&test_value](const decltype(*Mymap.begin()) &pair)
{
return any_of(pair.second.begin(), pair.second.end(), [&test_value](const string& str) { return str == test_value; });
});
if (iter1 != Mymap.end())
{
cout << "Key: " << iter1->first << endl;
}
else
{
cout << "No key found for " << test_value;
}
// Case 2
test_value = "unique3";
auto iter2 = Mymap.find(test_value);
if (iter2 != Mymap.end())
{
int first = true;
for (auto v : iter2->second)
{
cout << (first ? "" : ", ") << v;
first = false;
}
cout << endl;
}
else
{
cout << "No value found for key " << test_value << endl;
}
return 0;
}
The key is stored in pair.first. Just use that if your use-case is in loop iteration as you illustrated.
If you mean in any use, without iteration, that is, given a value obtain the associated key, there is not a direct way to do that. You could build inverse maps for each value to key but that would not be really efficient considering also the fact that you would also need unique values.
Create another map going the other way for every vector entry?
If the array entries are not unique, then you would need to do the same map-to-vector, or use multimap.
Also consider using hash map (unordered_map), and stringview as ways to reduce the memory usage of the second map?
But the best answer would be the boost 2-way map, sorry. You could wrap the two maps in your own class that exposes the functionality of a 2-way map.
I'm getting a strange error when trying to iterate a Map of Vectors. I'm using the Stanford CS106B class libraries for this project and when I try to compile the code I get an error telling me that "itr has no member named 'first' "
I have tried searching for solutions to this problem and I have found many similar entries but the answers seem to mimic what I'm doing. I'm sure I'm missing something simple...
#include <iostream>
#include <fstream>
#include <string>
#include "console.h"
#include "simpio.h" // for getLine
#include "strlib.h"
#include "vector.h"
#include "queue.h"
#include "map.h"
#include <iterator>
using namespace std;
void CountLetters(ifstream &filename) {
int index=0;
Vector<int> counts;
for (int i=0; i<=26; i++) {
counts.add(0);
}
char c;
while (!filename.eof()) {
c=filename.get();
index=c-'a';
if (index>=0 && index<26) {
c=stringToChar(toLowerCase(charToString(c)));
counts[index]++;
}
}
for (int y=0; y<=26; y++) {
cout << char('a'+y) << ": " << counts[y] << endl;
}
filename.close();
}
Map <string, Vector<char> > assembleSets (ifstream &in, int seed) {
Map <string, Vector<char> > letterSets;
char c;
Vector<char> workingText;
string letterSet;
while(!in.eof()) {
if (workingText.size()<seed) { // Build the intial set of "seed" letters.
c=in.get();
workingText.add(c);
}
else {
c=in.get();
letterSet.clear();
for (int i=0; i<workingText.size()-1; i++) {
letterSet+=workingText[i]; // add the letter to the letter set.
workingText[i]=workingText[i+1]; // move the letter down one in the vector (simulate queue).
}
letterSet+=workingText[seed-1];
workingText[seed-1]=c; // add the newwest letter to the workingText but do not add it to the letter set.
// Check to see if the letter set exists already, if not, add it.
if (!letterSets.containsKey(letterSet)) {
Vector<char> blank;
letterSets.add(letterSet,blank);
letterSets[letterSet].add(c);
}
else {
// Add the next character to the vector of characters for that letter set.
letterSets[letterSet].add(c);
}
}
}
return letterSets;
}
int main() {
ifstream in;
int mSeed =0;
while (true) {
string fileName = getLine("Please enter a file name");
in.open(fileName);
if(in.fail()) cout << "Couldn't open file!" << endl;
else break;
}
// CountLetters(in);
while (true) {
mSeed=getInteger("Enter a seed value: ");
if (mSeed>0&&mSeed<=10) {
break;
} else {
cout << "Please choose a value from 1 to 10." << endl;
}
}
Map<string, Vector<char> > letterSets = assembleSets(in, mSeed);
Map<string, Vector<char> > :: iterator itr;
for (auto& it: letterSets) {
string keys = (it.first);
Vector<char> values = it.second;
}
return 0;
}
Any help would be fantastic! I'm really scratching my head.
It simply means that Map<string, Vector<char> > :: iterator.
Using std::map instead of Map and std::vector instead of Vector compiles correctly.
Check the implementation of your custom iterator.
Anyway i suggest you using the range-based syntax for this (if you use the C++ standard library):
for (auto& it : letterSets)
{
string key = it.first;
vector<char> values = it.second;
}
I'm working on map and I have following nested map and initialized with some values:
map<string, map<int, int> > wordsMap;
map<int, int> innerMap;
map<int, int>::iterator iti;
for(int i = 2; i < argc; i++)
{
wordsMap[argv[i]].insert(pair<int, int>(0,0));
}
And after some processing I'm trying to change the content if inner map, I use following code:
while(some_condition)
{
i = 0
for( it = wordsMap.begin() ; it != wordsMap.end(); it++)
{
innerMap = it->second;
int cnt = count(words.begin(), words.end(), it->first);
if(cnt != 0){
wordsMap[it->first][i] = cnt;
}
}
i++;
}
In the above scenario, How to change the value of first key (i.e. "0") and its value used while initialization of the inner map with another key-value pair?
You can't change the key of an element in an std::map. Doing so would break ordering.
Instead, you must insert a new element in the map with the key you want, and delete the previous element from the map.
I'm not sure if I understand your intend. I assume you want to save,
<KEY : file_name, VALUE : <KEY : line, VALUE : words count>>
And you don't want to save second map if there is no words.
So, I wrote below code.
If you want to not present second map, just keep empty map through not inserting key-value.
Additionally, sincestd::map is an associative container, which means it is saved sorted based on Key value, you should try to avoid change the key value after saving it.
#include "stdafx.h"
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
typedef std::map<int, int> WORDS_COUNT_MAP_T; //for line, words count
typedef std::map<string, WORDS_COUNT_MAP_T> FILE_WORDS_COUNT_MAP_T; //for file name, WORDS_COUNT_MAP_T
int _tmain(int argc, _TCHAR* argv[])
{
FILE_WORDS_COUNT_MAP_T file_words_count_map;
//Input dummy data for test
//init file names
std::vector<string> file_names;
file_names.push_back("first");
file_names.push_back("second");
file_names.push_back("third");
//get and set words count in each file
for_each(file_names.begin(), file_names.end(), [&](const string& file_name)
{
//Just for test
WORDS_COUNT_MAP_T words_count_map;
if(file_name == "second")
{
//not find words, so nothing to do
}
else
{
words_count_map[0] = 10;
words_count_map[1] = 20;
}
file_words_count_map.insert(FILE_WORDS_COUNT_MAP_T::value_type(file_name, words_count_map));
});
//print
for_each (file_words_count_map.begin(), file_words_count_map.end(), [&](FILE_WORDS_COUNT_MAP_T::value_type& file_words_map)
{
cout << "file name : " << file_words_map.first << endl;
WORDS_COUNT_MAP_T words_count_map = file_words_map.second;
for_each (words_count_map.begin(), words_count_map.end(), [](WORDS_COUNT_MAP_T::value_type& words_map)
{
cout << "line : " << words_map.first << ", count : " << words_map.second << endl;
});
cout << "----" << endl;
});
getchar();
return 0;
}
This code will print like below,
I have an std::map and I want to search for a key using a substring. For example, I have the following code:
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
int main(int argc, char *argv[])
{
TStrStrMap tMap;
tMap.insert(TStrStrPair("John", "AA"));
tMap.insert(TStrStrPair("Mary", "BBB"));
tMap.insert(TStrStrPair("Mother", "A"));
tMap.insert(TStrStrPair("Marlon", "C"));
return 0;
}
Now, I want to search for the position that holds the substring "Marl" and not "Marlon", if "Marla" is stored in the map. I want to find something that starts with "Marl". I need to find at most one position. Is this possible? If so, how?
I don't want to use any Boost libraries!
You can't efficiently search for substring, but you can for prefix:
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
typedef map<string, string> TStrStrMap;
typedef pair<string, string> TStrStrPair;
TStrStrMap::const_iterator FindPrefix(const TStrStrMap& map, const string& search_for) {
TStrStrMap::const_iterator i = map.lower_bound(search_for);
if (i != map.end()) {
const string& key = i->first;
if (key.compare(0, search_for.size(), search_for) == 0) // Really a prefix?
return i;
}
return map.end();
}
void Test(const TStrStrMap& map, const string& search_for) {
cout << search_for;
auto i = FindPrefix(map, search_for);
if (i != map.end())
cout << '\t' << i->first << ", " << i->second;
cout << endl;
}
int main(int argc, char *argv[])
{
TStrStrMap tMap;
tMap.insert(TStrStrPair("John", "AA"));
tMap.insert(TStrStrPair("Mary", "BBB"));
tMap.insert(TStrStrPair("Mother", "A"));
tMap.insert(TStrStrPair("Marlon", "C"));
Test(tMap, "Marl");
Test(tMap, "Mo");
Test(tMap, "ther");
Test(tMap, "Mad");
Test(tMap, "Mom");
Test(tMap, "Perr");
Test(tMap, "Jo");
return 0;
}
This prints:
Marl Marlon, C
Mo Mother, A
ther
Mad
Mom
Perr
Jo John, AA
When your substring is a prefix as in your example, you can use lower_bound to search for "Marl".
map<string,string>::const_iterator m = tMap.lower_bound("Marl");
cerr << (*m).second << endl;
This does not work for non-prefix substrings: in the general case, searching a map is not much different from searching other containers.
I'd like to expand on the answer by Sergey by providing a full solution using map::lower_bound(). As mentioned in the comments on that answer, you have to check whether lower_bound() returns tMap.end(). If not, then you also have to check whether the found key is actually prefixed with the search string. Latter can be checked, for example, by using string::compare(). As a result, my C++11 solution looks as follows:
std::map<std::string, std::string> myMap{
{"John", "AA"}, {"Mary", "BBB"}, {"Mother", "A"}, {"Marlon", "C"}, {"Marla", "D"}
};
std::string prefix("Marl");
auto it = myMap.lower_bound(prefix);
if (it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0)
std::cout << it->first << ": " << it->second << std::endl;
Output:
Marla: D
However, if you want to find all keys in your map that are prefixed with the search string, then you can use the following loop:
for (auto it = myMap.lower_bound(prefix); it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0; ++it)
std::cout << it->first << ": " << it->second << std::endl;
Output:
Marla: D
Marlon: C
Code on Ideone
To search for a substring of a key in a map you have no choice but to either use a new map on a special kind of key type or to search your map in O(n). std::map uses (by default) operator<() for ordering keys and for searching, and that compare function for std::string is a plain lexicographical compare.
If you create a new map on a special key type that has operator<() compare on basis of a substring take note that this will also affect the decision of whether a new element to insert would be a duplicate. In other words, such a map will only have elements that are not substrings of each other.
The O(n) search practically means you use std::find() over the map, with a custom predicate that takes a std::pair<std::string,std::string> and returns true if the second element of the pair is a substring of the first.
typedef TStrStrMap::value_type map_value_type;
struct key_contains_substring
: std::binary_function<map_value_type, std::string, bool>
{
bool operator()(const map_value_type& map_value, const std::string& substr)
{
return std::search(map_value.first.begin(), map_value.first.end(),
substr.begin(), substr.end()) != map_value.first.end();
}
};
...
TStrStrMap::const_iterator it = std::find_if(tMap.begin(), tMap.end(),
std::bind2nd(key_contains_substring(), "Marl");