How to sort C++ map keys with std::greater? - c++

I'm creating a std::map<int, int> in C++ that I'd prefer to have they keys sorted from highest to lowest instead of the default sort order. My research lead me to std::greater which looked promising but when trying to use it I'm getting a compile error:
invalid type argument of unary ‘*’ (have ‘int’)
My map declaration is:
std::map<int, int, std::greater<int> > numMap;
And the error is getting thrown from this function:
void Row::addNumber(int num, int pos) {
numMap.insert(num, pos);
}
Answers to similar questions such as this include parenthesis in the declaration, i.e. std::greater() - but when I include those I get multiple errors regarding a function returning a function.

The problem – call of std::map::insert member function with invalid parameters: there are two integer values provided; but there must be std::pair<int, int>. Please see the reference: std::map::insert.
Preferable option
For convenience (just not to repeat the map type parameters), create a typedef for the map:
typedef std::map<int, int> IntMap;
The std::map has type definition for std::pair (pair representation) – std::map::value_type.
So, for example, if there is a std::map<int, int> the std::map::value_type would be std::pair<int, int>.
Use the std::map::value_type constructor (IntMap::value_type in this case):
class Row {
public:
void Row::addNumber(int num, int pos)
{
m_numMap.insert(IntMap::value_type(num, pos));
}
private:
typedef std::map<int, int> IntMap;
IntMap m_numMap;
};
Alternatives:
Use std::make_pair() function:
#include <utility>
...
void Row::addNumber(int num, int pos)
{
numMap.insert(std::make_pair(num, pos));
}
Directly use std::pair constructor:
void Row::addNumber(int num, int pos)
{
numMap.insert(std::pair<int, int>(num, pos));
}

A bit more pedantic than Sergey's answer (which also definitely works), instead Use:
typedef std::map<int, int, std::greater<int> > MyMap;
MyMap numMap;
void Row::addNumber(int num, int pos)
{
numMap.insert(MyMap::value_type(num, pos));
}
The benefit being that if you change the type of the map, you have less code to change later. And much less likely but still possible, if the implementation of std::map changes its value_type from std::pair to something else (in a future version of the stl), you are impervious to that change.

Related

Adding pair in set

I am trying to insert pair of int and string in my set. What i am trying to do is implement a hash map using sets, I am storing the number and its corresponding string using store function and then retrieving the string using retrieve. Please suggest what is the problem and is there any way it can be done more efficiently. The error is in store function. I have not written main function as its just taking input and calling the functions.
typedef pair<int, string> pairs;
pairs p;
set<pairs> s;
set<pairs>::iterator it;
int i=0;
void store(int num,string s)
{
p[i].first=num; //error is while I am using the pair to store the string
p[i].second=s;
s.insert(p[i]);
i++;
}
string retrieve(int num)
{
for(it=s.begin();it!=s.end();++it)
{
pairs m = *it;
if(m.first==num)
{
return m.second;
}
}
}
My error:
error: no match for ‘operator[]’ (operand types are ‘pairs {aka std::pair<int, std::basic_string<char> >}’ and ‘int’)
p[i].first=num;
pairs is a type alias for pair<int, string>. std::pair does not have an operator[] overload.
Assuming you want pairs to represent a collection of pairs, you need to make your pairs a container (e.g. a vector or an array).
As Martin Bonner noted in the comment, the symbol s inside store refers to the string s parameter. Change it to avoid clashing with set s's identifier.
typedef pair<int, string> pairs[100];
pairs p;
void store(int num, string str)
{
p[i].first=num;
p[i].second=str;
s.insert(p[i]);
i++;
}

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.

Search in vector<std::pair<int, vector<int> > >

I would like to search within a vector<std::pair<int, vector<int> > >. This won't work due to the vector parameters:
std::vector<std::pair<int, std::vector<int> > > myVec;
iterator = find(myVec.begin(), myVec.end(), i);
Search would be on the first std::pair template parameter (int), in order to get the vector associated with it.
std::vector<std::pair<int, std::vector<int> > > myVec;
This requires C++0x for the lambda expression:
typedef std::pair<int, std::vector<int>> pair_type
std::find_if(myVec.begin(), myVec.end(), [i](pair_type const& pair)
{ return pair.first == i; });
If you're not using C++0x then either roll out your own loop or use something like Boost.Phoenix/Boost.Lambda.
Or, for both cases, why not use std::map?
You could make do with the following (pretty ugly) functoid:
struct FindFirst {
FindFirst(int i) : toFind(i) { }
int toFind;
bool operator()
( const std::pair<int, std::vector<int> > &p ) {
return p.first==toFind;
}
};
using it like this ( I couldn't get bind2nd to work - that's why I used the c'tor ):
int valueToFind = 4;
std::find_if(myVec.begin(), myVec.end(), FindFirst(valueToFind));
I think what you would like is a map:
std::map< int, vector< int > > foo;
You can then add elements, search by key etc:
int key = 4; //This will be the key
vector<int> value(5, 4); //Fill some values (5 4's in this case) into the vector
foo[key]=value; //Adds the pair to the map. Alternatively;
foo.insert( make_pair(key, value) ); //Does the same thing (in this context)
Looking at the way you've done things though, you might be wanting a std::multimap (which allows multiple values to have the same key) Class docs here
You're trying to map an int to a vector of int.
So try map<int, vector<int> >.
The second template parameter of a vector is the allocator - your compiler can probably puzzle out what you wanted to say, the declaration is wrong anyway. What you probably want is some sort of map type, like iammilind suggested.

How can I use Boost.Bind on compound types?

I have std::map<int, std::pair<short, float> >, and I need to find the minimal short in this map. How can I use boost::bind with std::min_element() for this?
boost::lambda?
The map iterator will give you a pair where first is the int key and second is the map's pair value, so if you had an iterator it, you'd want the minimum of all the it->second.first values. The min_element function expects a comparison function for its third argument, so you need to build a comparison function that projects second.first of its two arguments.
We'll start with some typedefs to make the code more readable:
typedef std::pair<short, float> val_type;
typedef std::map<int, val_type> map_type;
map_type m;
We're going to use Boost.Lambda for its overloaded operators, allowing us to use operator<. Boost.Bind can bind member variables as well as member functions, so we'll take advantage of that, too.
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
using boost::bind;
// Comparison is (_1.second.first < _2.second.first)
std::cout <<
std::min_element(m.begin(), m.end(),
bind(&val_type::first, bind(&map_type::iterator::value_type::second, _1))
<
bind(&val_type::first, bind(&map_type::iterator::value_type::second, _2))
)->second.first;
That will also work with boost::lambda::bind.
min_element(map.begin(), map.end(),
compose2(less<short>(),
compose1(select1st<pair<short, float> >(),
select2nd<map<int, pair<short, float>
>::value_type>()),
compose1(select1st<pair<short, float> >(),
select2nd<map<int, pair<short, float>
>::value_type>()))
).second.first;
(Of course, somebody's going to complain that this is an abuse of STL and that these are extensions not in the C++ standard…)
bind cannot do this by itself, because first and second are exposed as fields, not methods (so you can't get away with something like mem_fun).
You could do this using your own functor of course though:
template <typename F, typename S>
struct select_first : std::binary_function<std::pair<F, S>&, F&>
{
F& operator()(std::pair<F, S>& toConvert)
{
return toConvert.first;
}
};

BOOST_FOREACH & templates without typedef

When I work with BOOST_FOREACH, there isn't a problem with simple templates as vector. But when I try to iterate through map > for example I need to typedef the element type.
Is there any workaround?
There is a problem because it is a macro, and therefore cannot handle types containing commas (preprocessor doesn't know about templates).
You can also declare the variable before the loop, see documentation.
std::map<int, double> my_map;
//1)
typedef std::pair<int, double> MyPair;
BOOST_FOREACH(MyPair p, my_map) { ... }
//2)
std::pair<int, double> p;
BOOST_FOREACH(p, my_map) { ... }
Edit:
There is a further complication with std::map in particular: the value_type is not std::pair<Key, Value>, but std::pair<const Key, Value>.
Hence, if you go with the typedef, a more proper way (and the only way if you want to use a reference in the foreach loop) is to use
typedef std::pair<const int, double> MyPair;
//or
typedef std::map<int, double>::value_type MyPair;
BOOST_FOREACH(MyPair& ref, my_map) { ... }
However, that won't work if you want to use a variable declared before the loop, since you can't assign to a std::pair<const int, double> instance later (can't assign to the const field), in which case you can only use pair<int, double> as boost's manual shows.
If you need to iterate over a map, the easiest way is to use tuples, since getting the correct type in order to typedef, is troublesome. Here is how you can use tuples:
std::map<int, double> my_map;
int key;
double value;
BOOST_FOREACH(boost::tie(key, value), my_map) { ... }
Just a note, the commas will work here because parenthesis are placed around key and value. The preprocessor only understands commas and parenthesis(and c99 requires it to understand quotation marks also). So, it can't parse the <> in std::pair<int, double>. However, we can use parenthesis to help the preprocessor. For example, if we have a a function that returns a container that is called like this:
BOOST_FOREACH(int i, foo<int, int>()) { ... } //This won't compile
So, we can place parenthesis around an expression and it will help the preprocessor:
BOOST_FOREACH(int i, (foo<int, int>())) { ... } //This will compile
However, in the case of a map, we can't place parenthesis around the declaration(because its not an expression). So this won't work:
BOOST_FOREACH((std::pair<int, double> p), my_map) { ... } //This won't work
Because it will get transformed into something like this (std::pair<int, double> p) = *it, and that, of course, is incorrect C++. But using a tie will work:
BOOST_FOREACH(tie(key, value), my_map) { ... } //This will work
We just need to declare key and value outside of the loop(as shown above). Plus, it can make the loop have more meaningful names. You can write key instead of p.first, and value instead of p.second.
BOOST_FOREACH_PAIR is another option that works well in our experience:
http://lists.boost.org/Archives/boost/2009/09/156345.php
http://lists.boost.org/Archives/boost/2009/09/156366.php
This can be as simple as this:
BOOST_FOREACH(auto& p, my_map) { ... }
Using the C++11 standard, auto is like var in C#, it will do
BOOST_FOREACH(std::pair<int, double>& p, my_map) { ... }