How to insert single existing pair into a map - c++

I have an already defined pair, and I want to insert it into a map. What are the correct way/s of doing that?
I tried two. One works. The other (which I mean to use, since it is more compact and clear) does not. There are no runtime errors, but it simply does not work (I checked this with the return value of insert, whose second member is False).
I guess the way to fix the second form is simple, but I could not find it.
This is what I have:
using namespace std;
typedef pair<string, char> pair_t;
typedef map<pair_t::first_type, pair_t::second_type> map_t;
typedef pair<map_t::iterator,bool> retval_insert_t;
int main(void) {
pair_t p2;
p2 = make_pair("Fer", 'C');
map_t grade_list;
grade_list[ "Jorge" ] = 'A';
grade_list.insert(make_pair("Alba", 'D'));
grade_list.insert(pair_t("Susi", 'C'));
grade_list.insert(make_pair(p2.first, p2.second)); // This works
grade_list.insert(p2); // This does not work
return 0;
}

Related

alias used not modifying the actual contents of STL container

I am unable to understand the following behaviour. When I use currPMap to modify the value, the value at the actual location is not modified. Why is that so.
I checked with the reference the operator[] and at() return reference and so this should have worked.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
typedef map<int, int> intMap;
typedef map<int, int>::iterator mapIt;
int main(void) {
vector< map<int, intMap > > b(2);
int curr=0, next=1;
map<int, intMap> currPMap = b.at(curr);
(currPMap[4])[2] = 3; //modified by currPMap.
cout<<((b.at(curr))[4])[2]<<endl;
((b.at(curr))[4])[2] = 3; //modified using the actual vector.
cout<<((b.at(curr))[4])[2]<<endl;
}
Output:
0
3
P.S.: I know what I am doing here can be achieved by many other ways in this setting, but this is not the actual program. This is just the explicit version of the problem that I am facing with my code. I would be grateful if someone answers what is wrong in this method.
Because you are getting a copy of a map here, not an alias:
map<int, intMap> currPMap = b.at(curr); // currMap is a copy of b[0]
You then modify the copy, not the map stored in the vector.
What you need is a reference:
map<int, intMap>& currPMap = b.at(curr); // currMap refers to b[0]
map<int, intMap> currPMap = b.at(curr);
That's not an alias (aka reference); that's a copy. If you want a reference, you need to declare it thusly:
map<int, intMap> & currPMap = b.at(curr);
^
Beware that the reference may be invalidated if you add or remove elements to the vector, since vectors need to move their elements to maintain a contiguous array.

How can I make an unordered set of pairs of integers in C++?

The following program does not compile an unordered set of pairs of integers, but it does for integers. Can unordered_set and its member functions be used on user-defined types, and how can I define it?
#include <unordered_set>
...
class A{
...
private:
std::unordered_set< std::pair<int, int> > u_edge_;
};
Compiler error:
error: no matching function for call to 'std::unordered_set >::unordered_set()'
There is no standard way of computing a hash on a pair. Add this definition to your file:
struct pair_hash {
inline std::size_t operator()(const std::pair<int,int> & v) const {
return v.first*31+v.second;
}
};
Now you can use it like this:
std::unordered_set< std::pair<int, int>, pair_hash> u_edge_;
This works, because pair<T1,T2> defines equality. For custom classes that do not provide a way to test equality you may need to provide a separate function to test if two instances are equal to each other.
Of course this solution is limited to a pair of two integers. Here is a link to an answer that helps you define a more general way of making hash for multiple objects.
Your code compiles on VS2010 SP1 (VC10), but it fails to compile with GCC g++ 4.7.2.
However, you may want to consider boost::hash from Boost.Functional to hash a std::pair (with this addition, your code compiles also with g++).
#include <unordered_set>
#include <boost/functional/hash.hpp>
class A
{
private:
std::unordered_set<
std::pair<int, int>,
boost::hash< std::pair<int, int> >
> u_edge_;
};
The problem is that std::unordered_set is using std::hash template to compute hashes for its entries and there is no std::hash specialization for pairs. So you will have to do two things:
Decide what hash function you want to use.
Specialize std::hash for your key type (std::pair<int, int>) using that function.
Here is a simple example:
#include <unordered_set>
namespace std {
template <> struct hash<std::pair<int, int>> {
inline size_t operator()(const std::pair<int, int> &v) const {
std::hash<int> int_hasher;
return int_hasher(v.first) ^ int_hasher(v.second);
}
};
}
int main()
{
std::unordered_set< std::pair<int, int> > edge;
}
As already mentioned in most of the other answers on this question, you need to provide a hash function for std::pair<int, int>. However, since C++11, you can also use a lambda expression instead of defining a hash function. The following code takes the solution given by Sergey as basis:
auto hash = [](const std::pair<int, int>& p){ return p.first * 31 + p.second; };
std::unordered_set<std::pair<int, int>, decltype(hash)> u_edge_(8, hash);
Code on Ideone
I'd like repeat Sergey's disclaimer: This solution is limited to a pair of two integers. This answer provides the idea for a more general solution.
OK here is a simple solution with guaranteed non collisions. Simply reduce your problem to an existing solution i.e. convert your pair of int to string like so:
auto stringify = [](const pair<int, int>& p, string sep = "-")-> string{
return to_string(p.first) + sep + to_string(p.second);
}
unordered_set<string> myset;
myset.insert(stringify(make_pair(1, 2)));
myset.insert(stringify(make_pair(3, 4)));
myset.insert(stringify(make_pair(5, 6)));
Enjoy!
You need to provide a specialization for std::hash<> that works with std::pair<int, int>. Here is a very simple example of how you could define the specialization:
#include <utility>
#include <unordered_set>
namespace std
{
template<>
struct hash<std::pair<int, int>>
{
size_t operator () (std::pair<int, int> const& p)
{
// A bad example of computing the hash,
// rather replace with something more clever
return (std::hash<int>()(p.first) + std::hash<int>()(p.second));
}
};
}
class A
{
private:
// This won't give you problems anymore
std::unordered_set< std::pair<int, int> > u_edge_;
};
The other answers here all suggest building a hash function that somehow combines your two integers.
This will work, but produces non-unique hashes. Though this is fine for your use of unordered_set, for some applications it may be unacceptable. In your case, if you happen to choose a bad hash function, it may lead to many unnecessary collisions.
But you can produce unique hashes!
int is usually 4 bytes. You could make this explicit by using int32_t.
The hash's datatype is std::size_t. On most machines, this is 8 bytes. You can check this upon compilation.
Since a pair consists of two int32_t types, you can put both numbers into an std::size_t to make a unique hash.
That looks like this (I can't recall offhandedly how to force the compiler to treat a signed value as though it were unsigned for bit-manipulation, so I've written the following for uint32_t.):
#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <utility>
struct IntPairHash {
std::size_t operator()(const std::pair<uint32_t, uint32_t> &p) const {
assert(sizeof(std::size_t)>=8); //Ensure that std::size_t, the type of the hash, is large enough
//Shift first integer over to make room for the second integer. The two are
//then packed side by side.
return (((uint64_t)p.first)<<32) | ((uint64_t)p.second);
}
};
int main(){
std::unordered_set< std::pair<uint32_t, uint32_t>, IntPairHash> uset;
uset.emplace(10,20);
uset.emplace(20,30);
uset.emplace(10,20);
assert(uset.size()==2);
}
You are missing a hash function for std::pair<int, int>>. For example,
struct bad_hash
{
std::size_t operator()(const std::pair<int,int>& p) const
{
return 42;
}
};
....
std::unordered_set< std::pair<int, int>, bad_hash> u_edge_;
You can also specialize std::hash<T> for std::hash<std::pair<int,int>>, in which case you can omit the second template parameter.
To make a unordered_set of pairs, you can either create a custom hash function or you can make an unordered_set of strings.
Create custom hash function: Creating the custom hash depends on the data. So there is no one size fits all hash function. A good hash function must have fewer collisions, so you need to consider the collision count while making the hash function.
Using Strings: Using string is very simple and takes less time. It also guarantees few or no collisions. Instead of using an unordered_set<pair<int, int>> we use an unordered_set. We can represent the pair by separating the numbers with a separator (character or string). The example given below shows how you can insert pair of integers with the separator (";").
auto StringPair = [](const pair<int, int>& x){return to_string(x.first) + ";" + to_string(x.second);};
unordered_set Set;
vector<pair<int, int>> Nums = {{1,2}, {2, 3}, {4, 5}, {1,2}};
for(auto & pair: Nums)
{
Set.insert(StringPair(pair));
}
Just to add my 2 cents here, it's weird that to use unordered_set you need to specify an external hash function. Encapsulation principle would prefer that inside your class you would have an 'hash()' function that returns the hash, and the unordered_set would call that. You should have an Hashable interface and your class, in this case std::pair, would implement that interface.
I think this is the approach followed by languages like Java. Unfortunately C++ doesn't follow this logic. The closest you can get to mimic that is:
derive a class from std::pair (this allows you to have more readable code anyway)
pass the hash function to the unordered_set template
Code Sample
class Point : public pair<int, int> {
public:
int &x = this->first; // allows to use mypoint.x for better readability
int &y = this->second; // allows to use mypoint.y for better readability
Point() {};
Point(int first, int second) : pair{first, second}{};
class Hash {
public:
auto operator()(const Point &p) const -> size_t {
return ((size_t)p.first) << 32 | ((size_t)p.second);
}
};
};
int main()
{
unordered_set< Point, Point::Hash > us;
Point mypoint(1000000000,1);
size_t res = Point::Hash()(mypoint);
cout<<"Hello World " << res << " " << mypoint.x;
return 0;
}
The simple hash function used works if size_t is 64bit and int is 32bit, in this case this hash function guarantees no collisions and it's ideal.

combining map and array of string c++

I'm writing a code to check if a person have visited a particular place before or not
if true do nothing else add the new place to the list of visited place
I'm using map to store the student name as a key and array the store places as follow
#include<map>
using namespace std;
map<string,string[]> stu_plac;
map<string,string[])::iterator it;
I could not find the roght way to search across the map
I tried the following:
bool exist=false;
if(stu_plac.count(name))
{
it=stu_plac.find(name);
for(auto tt=(*it).second.begin(); tt!=(*it).second.end(); tt++)
{
if (tt.compare(place)==0)//exist
{
exist=true;
}
break;
}
if (!exist)
{
(*it)second[++tt]=place;
}
}
else
{
stu_plac.insert(pair<string,string[]>(name,place));
}
I think the problem is with the array of string iterator, can you please help me to find the correct way to do this. Do I need to use multimap or other data structer to do this??
Data structure like map<string, vector<string> > will work.
You can use for (size_t i = 0; i < it->second.size(); i++) to traverse the vector.
It looks like you wanted a map of string -> places
typedef map<string,std::set<string> > Map;
Adapting it for your snippet (which had many problems, starting with the confusing formatting...):
#include<map>
#include<set>
#include<string>
using namespace std;
typedef map<string,std::set<string> > Map;
typedef Map::iterator It;
int main()
{
bool exist=false;
Map stu_plac;
string name = "name";
string place = "0.0.0.0";
It it = stu_plac.find(name);
if (stu_plac.end() != it)
{
std::set<string>& visits = it->second;
exist = visits.end() != visits.find(place);
if (!exist)
visits.insert(place);
}
}
But in reality you may prefer to use std::multimap
I wouln't use an array because I can't know it's size, I prefer using a vector or a list, and instead of using a boolean variable I would insert and then break the loop, so my algorithm would be like:
std::map<std::string, std::list<std::string> > stu_plac;
std::map<std::string, std::list<std::string> >::iterator it;
std::string myName("Edu");
std::string myPlace("Madrid");
.....
for( it = stu_plac.begin(); it != stu_plac.end(); it++){
if(it->first == myName){
std::list<std::string>::iterator place = std::find(it->second.begin(), it->second.end(), myPlace);
if(place == it->second.end()){
it->second.push_back(myPlace);
break;
}
}
}
Look that you can use the iterator you got to add the city you want.
By the way, I wouldn't use "auto" variable if it's not C++11.
An array in C++ is one of the types or constructs inherited from C so the built-in array "type" doesn't have any iterators. It also doesn't track its current size so you cannot get begin and end iterators of an array without also storing its size.
Use std::map< std::string, std::vector<std::string> > instead.

C++ find keys and values in a multimap

I want to create a structure that holds distinct strings and assign to each one of them some (not one unique) int values. After I have filled that structure, I want to check for each string how many different int have been assigned to and which exactly are they. I know that it is possible to tackle this with a multimap. However I am not sure if (or how) it is possible to get all the distinct strings contained to the multimap, since the function “find” requires a parameter for matching, while I do not know when searching which distinct values could be in the multimap. How could this be done with a multimap?
As an alternative solution I tried to use a simple map with a vector as value. However I still cannot make this work because the iterator of the vector does not seem to be recognized and it indicates me : iterator must a have a pointer to class type.
map<string, vector<int>>::iterator multit;
int candID1, candID2, candID3;
for(multit=Freq.begin(); multit!=Freq.end(); multit++)
{
if((*multit).second.size()==3)
{
vector<int> vectorWithIds = (*multit).second;
for(vector<int>::iterator it = vectorWithIds.begin();
it != vectorWithIds.end();it++)
{
candID1 = it-> Problem: The iterator is not recognized
}
}
}
Could anyone detect the problem? Is there an attainable solution, either on the first or the second way?
What is it->? It's vector if ints, you probably want *it.
P.S. I have to admit I haven't read the whole prose.
I suggest a multimap<string, int>. Assuming I understood your requirements correctly, with you having "unique" strings and several different values for them. You could use count(key) to see how many values there are for a key and equal_range(key) which returns a pair<interator, iterator> with the first iterator pointing to the start of the range of values for a key and second iterator pointing past the value for key.
See reference
Ok, this is totaly not efficient at all, but you can use std::set initialized with you std::vector to extract only the unique values of std::vector, like in this example:
#include <iostream>
#include <vector>
#include <map>
#include <set>
int main() {
// some data
std::string keys[] = {"first", "second", "third"};
int values[] = {1, 2, 1, 3, 4, 2, 2, 4, 9};
// initial data structures
std::vector<std::string> words(keys, keys + sizeof(keys) / sizeof(std::string));
std::vector<int> numbers(values, values + sizeof(values) / sizeof(int));
// THE map
std::map< std::string, std::vector<int> > dict;
// inserting data into the map
std::vector<std::string>::iterator itr;
for(itr = words.begin(); itr != words.end(); itr++) {
dict.insert(std::pair< std::string, std::vector<int> > (*itr, numbers));
} // for
// SOLUTION
// count unique values for the key of std::map<std::string, std::vector<int> >
std::map<std::string, std::vector<int> >::iterator mtr;
for(mtr = dict.begin(); mtr != dict.end(); mtr++) {
std::set<int> unique((*mtr).second.begin(), (*mtr).second.end());
std::cout << unique.size() << std::endl;
} // for
return 0;
} // main

std::map keys in C++

I have a requirement to create two different maps in C++. The Key is of type CHAR* and the Value is a pointer to a struct. I am filling 2 maps with these pairs, in separate iterations. After creating both maps I need find all such instances in which the value of the string referenced by the CHAR* are same.
For this I am using the following code :
typedef struct _STRUCTTYPE
{
..
} STRUCTTYPE, *PSTRUCTTYPE;
typedef pair <CHAR *,PSTRUCTTYPE> kvpair;
..
CHAR *xyz;
PSTRUCTTYPE abc;
// after filling the information;
Map.insert (kvpair(xyz,abc));
// the above is repeated x times for the first map, and y times for the second map.
// after both are filled out;
std::map<CHAR *, PSTRUCTTYPE>::iterator Iter,findIter;
for (Iter=iteratedMap->begin();Iter!=iteratedMap->end();mapIterator++)
{
char *key = Iter->first;
printf("%s\n",key);
findIter=otherMap->find(key);
//printf("%u",findIter->second);
if (findIter!=otherMap->end())
{
printf("Match!\n");
}
}
The above code does not show any match, although the list of keys in both maps show obvious matches. My understanding is that the equals operator for CHAR * just equates the memory address of the pointers.
My question is, what should i do to alter the equals operator for this type of key or could I use a different datatype for the string?
My understanding is that the equals operator for CHAR* just equates the memory address of the pointers.
Your understanding is correct.
The easiest thing to do would be to use std::string as the key. That way you get comparisons for the actual string value working without much effort:
std::map<std::string, PSTRUCTTYPE> m;
PSTRUCTTYPE s = bar();
m.insert(std::make_pair("foo", s));
if(m.find("foo") != m.end()) {
// works now
}
Note that you might leak memory for your structs if you don't always delete them manually. If you can't store by value, consider using smart pointers instead.
Depending on your usecase, you don't have to neccessarily store pointers to the structs:
std::map<std::string, STRUCTTYPE> m;
m.insert(std::make_pair("foo", STRUCTTYPE(whatever)));
A final note: typedefing structs the way you are doing it is a C-ism, in C++ the following is sufficient:
typedef struct STRUCTTYPE {
// ...
} *PSTRUCTTYPE;
If you use std::string instead of char * there are more convenient comparison functions you can use. Also, instead of writing your own key matching code, you can use the STL set_intersection algorithm (see here for more details) to find the shared elements in two sorted containers (std::map is of course sorted). Here is an example
typedef map<std::string, STRUCTTYPE *> ExampleMap;
ExampleMap inputMap1, inputMap2, matchedMap;
// Insert elements to input maps
inputMap1.insert(...);
// Put common elements of inputMap1 and inputMap2 into matchedMap
std::set_intersection(inputMap1.begin(), inputMap1.end(), inputMap2.begin(), inputMap2.end(), matchedMap.begin());
for(ExampleMap::iterator iter = matchedMap.begin(); iter != matchedMap.end(); ++iter)
{
// Do things with matched elements
std::cout << iter->first << endl;
}