I am trying to write a program in C++ using maps...
My goal is to avoid the same values repeated in maps.
If the keys are same, we can use maps to avoid the duplicated keys. To allow duplicate keys, we use multimaps.
In case the value is the same, how can we avoid that?
The program which I have written allows duplicated values:
typedef std::map<int, std::string> MyMap;
int main()
{
MyMap map;
MyMap::iterator mpIter;
int key;
string value;
int count;
for(count = 0; count < 3;count++)
{
cin >> key;
cin >> value;
std::pair<MyMap::iterator, bool> res = map.insert(std::make_pair(key,value));
}
for (mpIter=map.begin(); mpIter != map.end(); ++mpIter)
cout << " " << (*mpIter).second << endl;
}
Make the value part of the key and/or use a set but that may not really solve the problem. It isn't possible to easily define a container that has both unique keys AND values if that's what you want. However, you might still construct one. Here's a very simple example to illustrate what is needed:
// Assuming keys are KEY and values are VAL
class MyMap {
public:
std::set<KEY> keyset;
std::set<VAL> valset;
std::map<KEY,VAL> theRealMap;
// assuming existence of function HAS(S,V)
// which returns true if v is in set S
bool MyInsert(KEY ky, VAL val) {
if (HAS(keyset, ky) return false;
if (HAS(valset, val) return false;
keyset.insert(ky);
valset.insert(vl);
return theRealMap.insert(std::pair<KEY,VAL>(ky, val));
}
:
:
Since this is an example it's not intended to be copied. You will likely want to include the functionality provided by std:map. An easy way would be to use std::map as a base class but you will need to hide (by making private) or implement similar code for each variant of insert otherwise you might get inadvertent insert that may not be unique.
Note: this requires twice the size of a single map. You can save some space by using theRealMap instead of a separate set for keys set. Another way would be to search the map but that sacrifices time for space. It's your call.
One way to do this is to maintain a separate std::set of the values. When you insert a value into a set it returns a std::pair<iterator, bool>. The bool value is true if the value was not already in the set. This tells you if it is safe to also put the value in the map.
First, however, you need to make sure the key is unique because the same key may already have been inserted with a different value:
typedef std::map<int, std::string> MyMap;
int main()
{
MyMap map;
MyMap::iterator mpIter;
int key;
string value;
int count;
// keep track of values with a std::set
std::set<std::string> values;
for(count = 0; count < 3; count++)
{
cin >> key;
cin >> value;
auto found = map.find(key);
if(found != map.end()) // key already in map
continue; // don't add it again
// now try to add it to the set
// only add to the map if its value is not already in the set
if(values.insert(value).second)
map.insert(std::make_pair(key, value));
}
for(mpIter = map.begin(); mpIter != map.end(); ++mpIter)
cout << " " << (*mpIter).second << endl;
}
One (inefficient) way to do it is to create a reverse map (with <string,int>) and insert your input in reverse order as that of MyMap into it. If ok, then insert into MyMap
Here is the working code.
typedef std::map<int, std::string> MyMap;
typedef std::map<string, int> rev_Map;
int main()
{
MyMap map;
rev_Map rmap;
MyMap::iterator mpIter;
rev_Map::iterator rmap_iter;
int key;
string value;
int count;
for(count = 0; count < 3;count++)
{
cin >> key;
cin >> value;
std::pair<rev_Map::iterator, bool> ok = rmap.insert(std::make_pair(value,key)); //insert into the reverse map
if(ok.second) //if above amap.insert works
std::pair<MyMap::iterator, bool> res = map.insert(std::make_pair(key,value));
}
for (mpIter=map.begin(); mpIter != map.end(); ++mpIter)
cout << " " << (*mpIter).second << endl;
}
Related
I have the following problem - I want to count the occurrences of each word in a file. I'm using a map<string,Count> so the key is the string object representing the word, and the value being looked up is the object that keeps count of the strings so that :
class Count {
int i;
public:
Count() : i(0) {}
void operator++(int) { i++; } // Post-increment
int& val() { return i; }
};
The problem is that I want to use insert() instead of the operator[]. Here is the code.
typedef map<string, Count> WordMap;
typedef WordMap::iterator WMIter;
int main( ) {
ifstream in("D://C++ projects//ReadF.txt");
WordMap wordmap;
string word;
WMIter it;
while (in >> word){
// wordmap[word]++; // not that way
if((it= wordmap.find(word)) != wordmap.end()){ //if the word already exists
wordmap.insert(make_pair(word, (*it).second++); // how do I increment the value ?
}else{
...
}
for (WMIter w = wordmap.begin();
w != wordmap.end(); w++)
cout << (*w).first << ": "
<< (*w).second.val() << endl;
}
Could you refactor so as not to use find but simply attempt the insert?
Insert always returns a pair<iter*, bool>. The bool is 0 if it finds the key, and the iter* points to the existing pair. So we can take the pointer to the pair and increment the value:
// On successful insertion, we get a count of 1 for that word:
auto result_pair = wordmap.insert( { word, 1 } );
// Increment the count if the word is already there:
if (!result_pair.second)
result_pair.first->second++;
It was my first time posting. I'm learning C++ and welcome feedback on my idea.
The problem is that I want to use insert() instead of the operator[]
...why? std::map::insert cannot mutate existing values. operator[] is the right job for this.
If you really want to use insert (please don't), you first need to erase the existing value, if present:
if((it= wordmap.find(word)) != wordmap.end())
{
const auto curr = it->second; // current number of occurrences
wordmap.erase(word);
wordmap.insert(make_pair(word, curr + 1));
}
I have four static vectors. In my .cpp file (not my .h file!) I define these vector as such:
std::vector<Object*> ClassA::vecA;
std::vector<Object*> ClassA::vecB;
std::vector<Object*> ClassA::vecC;
std::vector<Object*> ClassA::vecD;
Then I populate each of these vectors with a number of objects of type Object.
Next I create a map:
std::map<std::string, std::vector<Object*> > cntr;
I populate this map with the vectors from above and a string as a Key for each vector.
The question is, how do I access the vectors in the map to find out the number of elements they contain. I have tried:
for (it = Cntr.begin(); it != Cntr.end(); it++)
{
if (it->first != token)
{
std::cout << it->first << std::endl;
int i = (it->second).size();
std::cout << "SIZE: " << i << std::endl;
}
}
However i always gives me the value of 1. What is the correct approach?
First off you need to set the iterator to point to an valid element of the map. When you do
std::map<std::string, std::vector<Object*>>::iterator Class::it;
int size = it->second.size();
it doesn't point to anything so using it is undefined behavior. What you can do though is use
std::map<std::string, std::vector<Object*>>::iterator Class::it;
it = cntr.begin();
int size = it->second.size();
Which now gives you the size of the first vector in the map.
If you want to get all of the sizes then you will need to iterate through the map. You can do this with a nice ranged based for loop like
for (const auto & elem : cntr) // get a const reference to each pair
std::cout << elem.second.size();
NathanOliver's answer should work if you have C++11. If you don't, you can try this, with a typedef to make the code clear:
typedef std::vector<Object*> MypObjVec;
typedef std::map<std::string, MypObjVec> MyMap;
MyMap::iterator Class::it = cntr.begin();
const MyMap::iterator Class::it_end = cntr.end();
for(; it!=it_end ; ++it)
{
std::cout<< it->second.size() << std::endl;
}
I have a set of strings that I have to put into a hash table and retrieve anagrams of it. I chose the unordered_map since it's an inbuilt hash table in c++. The strings are as followings,
cat, act, art, tar, rat... etc..
Now I used the alphabetically sorted word as key and a vector of unordered words as value. This implementation takes a lot of time in insertion. Is this the best possible implementation for the requirement, or is there something less time consuming that I can use?
std::tr1::unordered_map<std::string, std::vector<std::string>> map;
if(map.find(sortedWord) == map.end()){
std::vector<std::string> temp;
temp.push_back(word);
map.insert(make_pair(sortedWord, temp));
}else{
map.find(sortedWord)->second.push_back(word);
}
You're making that a lot more complicated than necessary, and in the process you're also slowing things down by:
Searching for the key twice, and
Copying a vector (with the contained word) when you have a new key. In fact, it is probably copied twice.
The following works fine with C++11, and I'm pretty sure it works the same way with tr1:
/* Name of map changed because I don't like to use "map"
* or "string" as variable names */
wordMap[sortedWord].push_back(word);
If sortedWord is not present in the map, then wordMap[sortedWord] will insert it with a default-constructed std::vector<std::string>>. So whether or not sortedWord was present, the new word can just be appended onto the value returned by the subscript.
Just to offer another solution, you may use C++11 std::unordered_multiset with customized hash algorithm and equality comparison.
The custom hash algorithm may simply combine the hash values of each characters with a commutative operation, say bitwise-xor, such that all anagrams have the same hash value.
The custom equality comparison can use std::is_permutation to equate all anagrams.
struct AnagramHash
{
typedef std::string argument_type;
typedef std::hash<char>::result_type result_type;
result_type operator()(const argument_type& s) const
{
std::hash<char> char_hash;
result_type result = 0;
for (const auto& x : s)
{
result ^= char_hash(x);
}
return result;
}
};
struct AnagramEqual
{
typedef bool result_type;
typedef std::string first_argument_type;
typedef std::string second_argument_type;
result_type operator()(const first_argument_type& lhs, const second_argument_type& rhs) const
{
if (lhs.size() == rhs.size())
return std::is_permutation(std::begin(lhs), std::end(lhs), std::begin(rhs));
else
return false;
}
};
int main()
{
std::unordered_multiset<std::string, AnagramHash, AnagramEqual> anagrams;
anagrams.insert("arc");
anagrams.insert("rac");
anagrams.insert("car");
anagrams.insert("H2O");
anagrams.insert("2OH");
auto range = anagrams.equal_range("car");
for (auto it = range.first ; it != range.second ; ++it)
{
cout << *it << endl;
}
cout << endl;
range = anagrams.equal_range("HO2");
for (auto it = range.first ; it != range.second ; ++it)
{
cout << *it << endl;
}
}
I need to store a number of key/value pairs and access them again referenced by key - not necessarily in a map, although this seems natural. Additionally, if the map exceeds a certain size, I need to delete the oldest pairs.
Is there a way to implement this using a map or a similar structure somehow combining a map and a queue in C++11?
UPDATE: I wanted to this with a std::unsorted_map. Unfortunately I'm heavily missing std::map functions which would help. The unordered list seems neither to support rbegin() nor does its iterator support the --operator, so that I can't use end() either.
Is there a better way than iterating through a loop to size()-1?
There's no unique solution for this problem, the simplest one would be to use an auxiliary queue for storing the keys in order of insertion.
map<string, string> m_myMap;
queue<string> m_myQueue;
void insert(const string& key, const string& value) {
m_myMap.insert(make_pair(key, value));
m_myQueue.push(key);
}
void deleteOldOnes() {
while (m_myQueue.size() > MAX_SIZE) {
m_myMap.erase(m_myQueue.front());
m_myQueue.pop();
}
}
You keep using the map for accessing the elements by key, the queue should not be used anywhere else than in the two methods above.
I had the same problem every once in a while and here is my solution: https://github.com/nlohmann/fifo_map. It's a header-only C++11 solution and can be used as drop-in replacement for a std::map.
Example
#include "src/fifo_map.hpp"
// for convenience
using nlohmann::fifo_map;
int main() {
// create fifo_map with template arguments
fifo_map<int, std::string> m;
// add elements
m[2] = "two";
m[3] = "three";
m[1] = "one";
// output the map; will print
// 2: two
// 3: three
// 1: one
for (auto x : m) {
std::cout << x.first << ": " << x.second << "\n";
}
// delete an element
m.erase(2);
// re-add element
m[2] = "zwei";
// output the map; will print
// 3: three
// 1: one
// 2: zwei
for (auto x : m) {
std::cout << x.first << ": " << x.second << "\n";
}
}
Note how the fifo_map's elements are always printed in the order of the insertion. Deletion of old elements is not implemented, but this extension should not be too difficult.
#include<iostream>
#include<queue>
using namespace std;
main(){
queue < pair <int,int> > Q; //First use a queue to store the pair wise values
int a,b;
// insert value to the queue as a pair
for (int i=0;i<6;i++){ // i only insert 6 pairs
cin>>a>>b;
if (Q.size()>=3){ // if queue size is 3 than pop up the first value
Q.pop();
}
Q.push(make_pair(a,b)); // insert a new pair into the queue
}
while(!Q.empty()){ // output the pairs on that queue
cout<<Q.front().first<<" "<<Q.front().second<<endl;
Q.pop();
}
return 0;
}
I have a map and I want the first column i.e (*it).first to be pushed back into a vector then (*it)->second to be pushed back into another vector
Is this the best way to do it?
std::vector<std::string>test;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
}
My other question is if i have a loop i.e
how would I insert all the integers i into (*it).first?
for(int i = 0; i < 10; i++)
{
// 1 - 10 will go in (*it).first
}
I want to have some integers in (*it).first and have associated values in (*it).second;
Use std::transform.
First define two functions key and value which take the pair of strings and return the first or second value, respectively.
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
Then use std::transform from <algorithm> with the functions to transform the map into either a vector of keys or a vector of values.
int main()
{
using namespace std; // be explicit normally, trying to be brief here
map<string, string> contacts;
contacts["alice"] = "555-2701";
contacts["bob"] = "555-2702";
vector<string> keys(contacts.size());
vector<string> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
cout << "Keys:\n";
copy(keys.begin(), keys.end(), ostream_iterator<string>(cout, "\n"));
cout << "\n";
cout << "Values:\n";
copy(values.begin(), values.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
Output:
Keys:
alice
bob
Values:
555-2701
555-2702
Your first question, "how can I push the first column of my map into one vector and the 2nd column into another" is solved thus:
std::map<std::string, std::string> mymap;
std::vector<std::string> keys;
std::vector<std::string> values;
for ( std::map<std::string,std::string>::iterator it=mymap.begin() ; it != mymap.end(); ++it )
{
keys.push_back(it->first);
values.push_back(it->second);
}
Your second question, "how would insert all the integers i into (*it).first ?" is solved thus:
std::map<int, int> mymap2;
for(int i = 0; i < 10; i++)
{
// Insert default value into map
// This sets '(*it).first' to 'i' and
// '(*it).second' to a default value (in
// this case, 0).
mymap2[i];
}
or
std::map<int, int> mymap3;
for(int i = 0; i < 10; i++)
{
// Insert specified value into map
// this sets '(*it).first' to 'i', and
// '(*it).second' to the value returned from the function.
maymap3[i] = ChooseSpecificValue(i);
}
Well, it can be done with a simple loop:
for (auto const& p: mymap) {
vec1.push_back(p.first);
vec2.push_back(p.second);
}
Or using the std::transform algorithm, though it's quite verbose here:
std::transform(mymap.begin(), mymap.end(), std::back_inserter(vec1),
[](MyMap::const_reference p) { return p.first; });
Assuming you've declared your map as string key and value (ie map<string, string> mymap; then it would be like below, also assuming you've declare 'it' variable as map<string, string>::iterator it, etc:
std::vector<std::string> test;
std::vector<std::string> second;
std::map<string, string>::iterator it;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
second.push_back((*it).second);
}
Not sure about your next question.
The first part of your question:
std::vector<std::string> test;
std::vector<std::string> test2; // assuming map is from string to string
for (it = mymap.begin(); it != mymap.end(); ++it)
{
test.push_back(it->first); // push first in one vector
test2.push_back(it->second); // push second in another vector
}
So, yes a simple for can do what you want.
The second part of your question:
Since you are updating the key of the map, you would need to remove it from the map and insert the changed one. So:
std::string first, second;
first = it->first;
second = it->second;
mymap.erase(it); // be careful with invalidating iterator
// change first
mymap[first] = second;
To change first by adding all integers i to it, that would really depend on the type of first. For example with a string, you may mean something like this:
ostringstream sout;
for (int i = 0; i < 10; ++i)
sout << (i?" ":"") << i;
first = sout.str();
Or if first is for example a set, you may mean something like this:
for (int i = 0; i < 10; ++i)
first.insert(i);
and my other question is if i have a loop i.e how would insert all the
integers i into (*it).first?
In the case of a std::map, you can't modify the iterator returned like that ... the key member (i.e., the first) in the std::map key/value pair data-structure is intentionally designated as a constant value, and is initialized to its constant value at the beginning of the key/value pair's lifetime in the std::map data-structure. If the keys weren't constant, you would end up creating havoc when you change the key, since the nodes in a std::map are suppose to be sorted by the keys. The second member of the key/value pair data-structure is the member that can be changed.
So if you want to insert a set of key/value pairs in a map, you could simply do the following:
std::map<int, int> mymap;
int some_other_value = 100;
for (int i=0; i < 10; i++)
{
mymap[i] = some_other_value++;
}
it here will be an iterator which will point to one of the position in map and at max have one first and second value for one iterator . At max you can have multiple key or same key holding same/different values depending on key/value combination.
As far as pushing the value in the vector for a key in map is concern you can do it in the same way you are pushing the key
std::vector<std::string>test;
std::vector<std::string>test2;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
test2.push_back((*it).second);
}
Neways yours question is very unclear .
Just in case you want to deal with different data types in your map I would template a generic copy function:
template <class A, class B>
void mycopy(std::map<A, B>&m, std::list<A>& keys, std::list<B>& values) {
typename std::map<A, B>::iterator it;
for (it = m.begin(); it != m.end(); ++it) {
keys.push_back( (*it).first );
values.push_back( (*it).second );
}
}
Mixing it up:
std::map<int, std::string> mymap;
std::list<int> keys;
std::list<std::string> values;
mymap[1] = "string1";
mymap[2] = "string2";
mycopy(mymap, keys, values);
std::map<std::string, int> mymap1;
std::list<std::string> keys1;
std::list<int> values1;
mymap1["string1"] = 1;
mymap1["string2"] = 2;
mycopy(mymap1, keys1, values1);
Edit: yes __copy isnt the best definition. Thanks