Find number of occurrences in a map C++ - c++

I am trying to create a map in C++ and I will use it to compare wheater a vector of string match another one. The condition is that only one word can be taken into consideration. For example, given:
{"two", "times", "three", "is", "not", "four"}
{"two", "times", "two", "is", "four"}
In this case they shouldn't match because there is only one "two" in the first vector.
My code is as follows:
#include <iostream>
#include <vector>
#include <map>
using namespace std;
void checkMagazine(vector<string> magazineWords, vector<string> noteWords)
{
map<string, int> magazine;
map<string, int>::iterator it = magazine.begin();
//populating the map
for (string magazineWordString : magazineWords)
{
it = magazine.find(magazineWordString);
if (it != magazine.end())
{
int numberOfOccurences = it->second;
magazine.insert(pair<string, int>(magazineWordString, numberOfOccurences + 1));
}
else
{
magazine.insert(pair<string, int>(magazineWordString, 1));
}
}
//checking for correspondences
for (string noteWordString : noteWords)
{
it = magazine.find(noteWordString);
if (it != magazine.end())
{
int numOfOccurences = it->second;
magazine.insert(pair<string, int>(noteWordString, numOfOccurences - 1));
}
else
{
cout << "There is no match." << endl;
return;
}
}
cout << "There is a match!" << endl;
}

There's a far easier way! The subscript operator on a map will try to find the key in the map. If it is successful, it returns a reference to its value. Otherwise, it creates a new key/value pair for you on the spot and gives you a reference to its value.
So since the default value of an int will be 0 (that is, when we create a new key/value pair, the value will be 0), all your work can actually just be:
bool checkMagazine(vector<string> magazineWords, vector<string> noteWords)
{
map<string, int> bank;
//populating the map
for (string magazineWord : magazineWords) {
++bank[magazineWord];
}
//checking for correspondences
for (string noteWord : noteWords) {
if(--bank[noteWord] < 0) { return false; }
}
return true;
}
Watch it run here: https://ideone.com/MzLAJM

Related

I don't understand why my sort on a string breaks everything

I have the following code:
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;
vector<vector<string>> findAnagrams(vector<string> wordlist) {
vector<vector<string>> result;
unordered_map<string, vector<string>*> indexes;
for (const string& word : wordlist) {
string wordSorted = word;
sort(wordSorted.begin(), wordSorted.end()); // <= This line break everything
auto index = indexes.find(wordSorted);
if (index == indexes.end()) {
vector<string> vec = { word };
result.push_back(vec);
indexes[wordSorted] = &vec;
} else {
index->second->push_back(word);
}
}
return result;
}
int main()
{
vector<string> wordlist = {"eat", "tea", "tan", "ate", "nat", "bat", "test", "estt"};
auto result = findAnagrams(wordlist);
for (const auto& vec : result) {
for (const auto& word : vec) {
cout << word << " ";
}
cout << endl;
}
return 0;
}
This code detects all anagrams in a list of given words.
As my comment says, when I sort wordSorted using std::sort, it breaks everything and my code ends with a bad_alloc. As if the std::sort manipulates the memory outside of wordSorted. If I remove this specific line, the code "works" (the result is obviously wrong, but it does what it should do).
How it is possible? What am I missing?
I'm guessing these lines are the main cause of your problem:
{
vector<string> vec = { word };
result.push_back(vec);
indexes[wordSorted] = &vec;
}
Here you store a pointer to the local variable vec in the indexes map. When the block ends at } the life-time of vec also ends, and the pointer you just stored will become invalid.
Any use of this pointer will lead to undefined behavior.
It seems to me that the solution is to simply not store pointers to the vector (pointers to containers are seldom, if ever, needed), and instead store a copy.

get the element from a Map with a given value in the Value

I have a map defined as std::map<std::string, textInfo> tempMap;
the textInfo class has some attributes as textsize,textcolor,textfont etc..
I want to select an item from this map that matches with a given value to an attribute in textInfo class.
for example if the Map contains
<"A",textInfo("10","Red","Verdana")>
<"B",textInfo("12","Green","Timesnewroman")>
<"C",textInfo("11","Blue","Cambria")>
I want to select the item that contains "Cambria" in it textfont attribute.
<"C",textInfo("11","Blue","Cambria")>
std::find_if should work for your needs.
Sample program:
#include <iostream>
#include <map>
#include <algorithm>
struct textInfo
{
std::string textsize;
std::string textcolor;
std::string textfont;
};
int main()
{
std::map<std::string, textInfo> m =
{
{"A", {"10","Red","Verdana"}},
{"B", {"12","Green","Timesnewroman"}},
{"C", {"11","Blue","Cambria"}}
};
auto iter = std::find_if(m.begin(),
m.end(),
[](std::pair<std::string, textInfo> const& item)
{ return (item.second.textfont == "Cambria");});
if ( iter != m.end() )
{
auto& item = iter->second;
std::cout << item.textsize << ", " << item.textcolor << ", " << item.textfont << std::endl;
}
}
Output:
11, Blue, Cambria
You can only access maps directly via a key, here your std::string. To search for a value or even a variable inside a value like it's the case here you have to iterate over the whole map.
std::map<std::string, textInfo>::const_iterator it = tempMap.begin();
for (; it != tempMap.end(); ++it)
{
if (0 == tempMap[(*it)].textfont.equals("Cambria")) // You could use == operator if it's a std::string
{
break; // found
}
}
// Do something with the found item. If the iterator is tempMap.end(), nothing found!
Look here for more informations.

finding a value in a vector, which in turn is present inside a structure

Finding an element in a vector of structures
this link showed me, how to look for a value inside a structure.
but i have something like this,
struct sample {
string name;
vector<string> values;
};
vector<sample>v1;
and this is a vector of structures. how to search for a particular string in the values vector, that is present inside the structure samples ? which itself, is a vector of structures ?
thanks.
You can iterate through the vector v1 containing sample structures accessing each vector v1 member as a struct. Then, you can access the struct member vector to search for desired string:
for (const sample &it : v1) {
for (const string &st : it.values) {
if (st == ...) {
}
}
}
You can use a combination of std::find_if and std::find.
The std::find_if goes through the sample objects and checks every element with a predicate which itself uses std::find to go through all std::string elements inside and compares each of them to the token you want to find.
Here is an example, using a lambda function to create the predicate:
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>
struct sample
{
std::string name;
std::vector<std::string> values;
};
int main()
{
std::vector<sample> const v1 =
{
{ "one", { "a", "b" } },
{ "two", { "c", "token to find", "d", "e" } },
{ "three", { "f"} }
};
using std::begin;
using std::end;
auto const token = "token to find";
// go through all samples
auto const sample_iter = std::find_if(begin(v1), end(v1), [&token](sample const& s)
{
// in each sample, go through all values
auto const string_iter = std::find(begin(s.values), end(s.values), token);
return string_iter != end(s.values);
});
if (sample_iter == end(v1))
{
std::cout << "not found\n";
}
else
{
std::cout << sample_iter->name << '\n';
}
}
Output:
two

c++ return string based on pair of integers

I have a list of sites, e.g. site1, site2, site3, etc (a rather long one at that) that are mapped to a pair of single-digit integers that my program receives from it's remote client, and I need an efficient way to return (as a string) the site name based on this pair of integers. The first digit is significant by itself, and the second digit is not significant unless paired with the first. These "site codes" should each return a unique string.
Here's how I'm doing it currently:
#include<string>
#include<iostream>
#include<vector>
// sbits is a vector of integers from which these two integers are being pulled
std::string readSite( vector<int> sbits ) {
int rgcode = sbits[5];
int uid = sbits[6];
if ( rgcode == 0 ) {
if ( uid == 0 ) {
return "site1";
}
else if ( uid == 1 ) {
return "site2";
}
else if ( uid == 2 ) {
return "site3";
}
// throw an exception if it's not here
else {
std::throw 10;
}
}
else if ( rgcode == 1 ) {
if ( uid == 0 ) {
return "site4";
else if ( uid == 1 ) {
return "site5";
else {
std::throw 10;
}
}
else {
std::throw 5;
}
std::catch( int err ) {
std::cout << "An exception has occurred. Error "<< err << " closing." << std::endl;
exit;
}
}
Everything about this makes me die a little inside. It's tiresome to write, tiresome to read, and probably sub-optimal for what I need to do.
So my question is this: Is there a more elegant (and less suicide-inducing) way to do this?
I've been reading about std::enum, std::map, but they don't seem to fit what I'm trying to do here.
edit: Is there a way to do this using an ordered list of some sort for the sites? Something so I don't have to go through and write 70+ variations of the same line. Some way to iterate through each? Maybe?
You need to define data structure properly to simply your code:
typedef std::pair<int, int> RgCodeUidPair;
// rgcode, uid
Then you can search through cachedData map by using (rgcode, uid) as pair.
std::string readSite( int rgcode, int uid)
{
static std::map<RgCodeUidPair, std::string> cachedData; // cache data, only load once
if (cachedData.empty())
{
cachedData.insert(make_pair(make_pair(0, 0), "site1"));
cachedData.insert(make_pair(make_pair(0, 1), "site2"));
cachedData.insert(make_pair(make_pair(1, 0), "site3"));
cachedData.insert(make_pair(make_pair(1, 1), "site4"));
}
auto it = cachedData.find(make_pair(rgcode, uid));
if (it != cachedData.end())
{
return it->second;
}
// throw if not found
}
Another way of looking at things:
#include <array>
#include <vector>
std::string const& readSite(std::vector<int> const& sbits ) {
static std::array<std::array<std::string, 10>, 10> const strs{{
{{ "site1", "site2", "site3" }},
{{ "site4", "site5" }}
}};
return strs.at(sbits.at(5)).at(sbits.at(6));
}
A few points
Needs error handling for empty strings (can be handled upstream if necessary)
Note how the vector is passed by reference
Throw an actual exception class instead of an int.
A rather simple way is to reduce the pair of int to a unique string and then use to store the sites.
#include <iostream>
#include <string>
#include <map>
#include <sstream>
using namespace std;
typedef map <string, string> SiteMap;
typedef pair<int, int> IntPair;
SiteMap map;
string ConvertPairToStr (IntPair const& pair)
{
int first = pair.first;
int second = pair.second;
stringstream unq;
unq << first;
unq << "_";
unq << second;
return unq.str();
}
void StoreSite (string site, IntPair p)
{
string unq = ConvertPairToStr(p);
map [p] = site;
}
string GetSite (IntPair p)
{
string unq = ConvertPairToStr(p);
// Assuming site is already stored
return map[p];
}

How to use iterator to update element of 2D string vector

See this code. I need to change the value of a specific element in a 2D string vector using the iterator. I can use for loop with an index to do this. but here what i need is directly use the iterator to refer the element. something like (*ite)[0] = "new name"
Any idea? I added full working code here for your convenience
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
string convertInt(int number)
{
stringstream ss;
ss << number;
return ss.str();
}
int main()
{
vector<vector<string>> studentList;
for(int i=0; i<10; i++){
vector<string> student;
student.push_back("name-"+convertInt(i));
student.push_back(convertInt(i+10));
studentList.push_back(student);
}
vector<string> temp;
for(vector<vector<string>>::iterator ite = studentList.begin(); ite != studentList.end(); ++ite){
temp = *ite;
if(temp[0].compare("name-5")==0){
cout << "Changeing the name of student 5" << endl;
// I need to change the studentList[5][0] (original value not the one in temp vector) at here using iterator ite
}
}
return 0;
}
Because temp = *ite makes a copy of vector(student), if you modify on temp, it's not modified on real studentList, that's why you need (*ite)[0] = "new name" to change value on real element.
Using for loop is a bit "ugly", use std::find_if instead of for loop:
bool IsFindFifthName(const std::vector<std::string>& student)
{
return student[0] == "name-5";
}
std::vector<std::vector<std::string>>::iterator iter
= std::find_if(studentList.begin(), studentList.end(), IsFindFifthName);
if (iter != studentList.end() )
{
(*iter)[0] = " new name";
}
Or use Lambda if C++11 is available:
std::vector<std::vector<std::string>>::iterator iter
= std::find_if(studentList.begin(), studentList.end(),
[](std::vector<std::string>& student){ return student[0] == "name-5"; });
if (iter != studentList.end() )
{
(*iter)[0] = " new name";
}
Using STL algorithm Transform in case could a good option. The algorithm internally uses iterators. A Sample example:
typedef std::vector<std::string> VectorOfString;
void DisplayStudent(const std::vector<VectorOfString>& StudentList)
{
std::for_each(StudentList.cbegin(), StudentList.cend(),
[](const VectorOfString& vectorElement)
{
std::for_each(vectorElement.cbegin(), vectorElement.cend(),
[](const std::string& value)
{
std::cout << value << endl;
});
});
}
std::vector<VectorOfString> StudentList;
std::string data1 = "One";
std::string data2 = "Two";
VectorOfString data(2);
data.push_back(data1);
data.push_back(data2);
StudentList.push_back(data);
DisplayStudent(StudentList);
std::for_each(std::begin(StudentList), std::end(StudentList),
[](VectorOfString& vectorElement)
{
std::transform(std::begin(vectorElement), std::end(vectorElement), std::begin(vectorElement),
[](std::string& value)-> std::string
{
if(value.compare("One") == 0)
return "OneOne";
else
return value;
});
});
DisplayStudent(StudentList);