I'm trying to understand operator overloading used in case of STL class templates, such as: map or pair.
Let me introduce you to my code:
#include <iostream>
#include <iomanip> // left, setw
#include <string>
#include <map>
#include <utility> // pair
#include <algorithm> // count if
using namespace std;
typedef pair <string, int> Emp;
typedef map <string, Emp> MAP;
class Zakr{
int min, max;
public:
Zakr(int min, int max): min(min), max(max){}
bool operator()(const pair<const string, Emp> &p) const{
int wage = p.second.second;
return (min < wage) && (wage < max);
}
};
void print (const MAP& m) {
MAP::const_iterator it, fin = m.end();
for(it = m.begin(); it != fin; it++)
cout << "Key: " << left << setw(7) << it -> first
<< "Name: " << setw(10) << it->second.first
<< "Wage: " << it->second.second << endl;
}
int main(void){
MAP emp;
MAP object;
emp["John"] = Emp("John K.", 1900);
emp["Tania"] = Emp("Tania L.", 1900);
emp["Jeremy"] = Emp("Jeremy C", 2100);
emp["Susie"] = Emp("Susie W.", 3100);
emp["Toto"] = Emp("Toto T.", 9900);
emp["Adrian"] = Emp("Adrian N.", 1600);
emp["Germy"] = Emp("Germy P.", 2600);
print(emp);
int mn = 0, mx = 2000;
int how_much = count_if(emp.begin(), emp.end(), Zakr(mn, mx));
cout << how_much << " earn from"
<< mn << " to " << mx << endl;
}
I'm struggling to understand some bits, especially one in particular, i.e.:
class Zakr{
int min, max;
public:
Zakr(int min, int max): min(min), max(max){}
bool operator()(const pair<const string, Emp> &p) const{
int wage = p.second.second;
return (min < wage) && (wage < max);
}
};
So I build class called Zakr, so that I will be able to use it to determine as a functor in count_if statement.
Am I right ?
I initialize private fields, min and max to use them in constructor and than so that operator which has been overloaded could return the boolean value based on their own values.
The most diffucult part is to understand bool operator overloading.
bool operator()(const pair<const string, Emp> &p) const{
int wage = p.second.second;
Why the hell I need to 1* make a pair of some useless string value and EMP?
Since all I'm interested in is stored in EMP, i.e.: int value which will be used in overloading is stored in Emp.
Why couldn't I just access int stored in Emp like so:
bool operator(Emp &p)
{
int wage = p.second;
return (min < wage) && (wage < max);
}
Why I do I need to make another pair like so: (const pair &p), if all I'm interested in is values stored in pair called Emp.
Why do I need to make another pair with useless first element of above named pair: string.
I'm not going to make use of it, so why it's needed to compile the code ?
I did try my best to explain my doubts as clear as its possible.
Hopefully someone will understand this rather long post.
Cheers!
This is because iterators over std::map return you a std::pair for each element. The first item in the pair is the map key, the second item is the map value. See the value_type in the documentation for std::map.
This question has some answers on how to get iterators over the map's values only.
1. Class Zakr:
Indeed, it's typically for use with count_if(). If you look at the link, you'll see count_if(first, last, pred) is equivalent to something like:
int ret = 0;
while (first!=last) {
if (pred(*first)) ++ret; // works as soon as object pred has operator() defined.
++first;
}
return ret;
2. Why is a pair needed in operator():
A map works with pairs, each made of a unique key and a corresponding value.
This is in part hidden. For example, when you use a map as associative array with expression as emp["John"], the map will find the pair having the unique key "John" and return a reference to the corresponding value.
However, as soon as you iterate through the map, your iterator will address these pairs. Why ? Because if it would just iterate through the value, you would get value, but you would never know to which unique key it corresponds.
Consequence: count_if() iterates through the map, so the predicate is called with an iterator that addresses a pair.
3. Why make a useless pair:
First, the counting function does not create a dummy pair. It uses a reference to an existing pair (from performance point of view, it's no more cost than passing a pointer !)
And, well, the map is here to address general problems. You could one day be interested to make count not only on wage, but for example also on the associated key (example: all the wages no in the range for employees with name starting with 'A').
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));
}
Given
std::vector<std::pair<std::pair<int,int>, std::pair<int,int>> a;
std::sort(a.begin(),a.end());
How is std::sort function going to sort this type of vector? Is there any generalization for further cascading of pairs?
std::sort uses operator< unless specified otherwise.
According to the documentation of std::pair:
http://en.cppreference.com/w/cpp/utility/pair/operator_cmp
Comparison operators, such as < are defined as the lexicographical equivalents:
Compares lhs and rhs lexicographically, that is, compares the first elements and only if they are equivalent, compares the second elements.
In your case, this logic will be applied recursively at each pair level.
It will sort by first element of the first pair of pairs. In another word, the most left value of pair of pairs.
See this simple example.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> Pair;
typedef pair<Pair, Pair> PPair;
ostream& operator<< (ostream& in, pair<int, int> p)
{
in << '(' << p.first << ", " << p.second << ')';
return in;
}
int main(void)
{
vector<PPair> vPP;
PPair pp1(Pair(123, 2), Pair(8, 7));
PPair pp2(Pair(33, 22), Pair(88, 77));
PPair pp3(Pair(333, 222), Pair(888, 777));
vPP.push_back(pp1);
vPP.push_back(pp3);
vPP.push_back(pp2);
std::sort(vPP.begin(), vPP.end());
vector<PPair>::const_iterator cit;
for (cit = vPP.begin(); cit < vPP.end(); cit++) {
cout << (*cit).first << (*cit).second << endl;
}
return 0;
}
Let our vector be Q
vector <pair<int,pair<int,int>>> Q;
Then for Sorting
sort(Q.begin(),Q.end(),compare);
Compare function result the boolean as per the desired condition
i.e This will sort the vector in
Ascending order
bool compare(pair<int,pair<int,int>> a,pair<int,pair<int,int>> b)
{
if(a.second.first<=b.second.first)
return true;
else
return false;
}
Descending order
bool compare(pair<int,pair<int,int>> a,pair<int,pair<int,int>> b)
{
if(a.second.first>=b.second.first)
return true;
else
return false;
}
So,you can design condition as per your requirement.
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 create a lookup table which links a length to a time interval (both are of data type double). The keys increment linearly as they are inserted, so it will already be sorted (perhaps an unordered_map would be better?).
What I am looking for is a way to find a key that best matches the current length provided to get the time value, or even better find the two keys that surround the length (the given key is between them) so I can find the interpolated value between the two time values.
I also need the best performance possible as it will be called in real time.
EDIT: I would have rather the following was a comment to the first answer below, but the format is hard to read.
I tried to do the following, but it seems to return the same iterator (5.6):
std::map<double, double> map;
map.insert(std::pair<double, double>(0.123, 0.1));
map.insert(std::pair<double, double>(2.5, 0.4));
map.insert(std::pair<double, double>(5.6, 0.8));
std::map<double, double>::iterator low, high;
double pos = 3.0;
low = map.lower_bound(pos);
high = map.upper_bound(pos);
How would I get 'low' to point to the last element that is < than the key used to search?
EDIT 2:
Silly me, 'low--' will do it, providing it's not the first element.
Getting there :)
For this, you can use either std::map::lower_bound
Returns an iterator pointing to the first element that is not less than key.
or std::map::equal_range
Returns a range containing all elements with the given key in the container.
In your case, if you want the closest entry, you need to check both the returned entry and the one before and compare the differences. Something like this might work
std::map<double, double>::iterator low, prev;
double pos = 3.0;
low = map.lower_bound(pos);
if (low == map.end()) {
// nothing found, maybe use rbegin()
} else if (low == map.begin()) {
std::cout << "low=" << low->first << '\n';
} else {
prev = std::prev(low);
if ((pos - prev->first) < (low->first - pos))
std::cout << "prev=" << prev->first << '\n';
else
std::cout << "low=" << low->first << '\n';
}
"best performance possible" - given you insert elements in increasing order, you can push_back/emplace_back them into a std::vector then use std::lower_bound - you'll get better cache utilisation because the data will be packed into contiguous address space.
You could of course use lower_bound and upper_bound, which are logarithmic in runtime. And they should do what you want.
std::map<double,double>::iterator close_low;
//... your_map ...
close_low=your_map.lower_bound (current_length);
This should give you an iterator to the the first map element whose key is < current length. Do likewise with upper_bound and you have your time surrounded.
The functions std::lower_bound() and std::upper_bound() would be useful here.
lower_bound() gives the first element that is >= to the value you're looking for; upper_bound() gives the first element that is > than the value.
For instance, searching for the value 5 in the following list: {1,3,5,5,6}1 using lower_bound() returns the third element, while upper_bound() would return the fifth element.
If the two functions return the same thing x, then the value you're looking for is not present in the list.
The value just before it is x-1 and the value just after it is x.
1As pointed out by Tony D in a comment, the question asked for maps, which generally do not contain duplicate elements.
I'm keeping this example though to illustrate the two functions.
Complete generic solution (original idea taken from Olaf Dietsche's answer):
#include <map>
#include <iostream>
#include <cstdint>
template <typename T1, typename T2>
T1 findClosestKey(const std::map<T1, T2> & data, T1 key)
{
if (data.size() == 0) {
throw std::out_of_range("Received empty map.");
}
auto lower = data.lower_bound(key);
if (lower == data.end()) // If none found, return the last one.
return std::prev(lower)->first;
if (lower == data.begin())
return lower->first;
// Check which one is closest.
auto previous = std::prev(lower);
if ((key - previous->first) < (lower->first - key))
return previous->first;
return lower->first;
}
int main () {
double key = 3.3;
std::map<double, int> data = {{-10, 1000}, {0, 2000}, {10, 3000}};
std::cout << "Provided key: " << key << ", closest key: " << findClosestKey(data, key) << std::endl;
return 0;
}
#include <map>
template <typename T1, typename T2>
std::map<T1, T2>::iterator nearest_key(const std::map<T1, T2>& map, T1 key) {
auto lower_bound = map.lower_bound(key);
auto upper_bound = lower_bound; upper_bound++;
if (lower_bound == map.end()) return upper_bound;
if (upper_bound == map.end()) return lower_bound;
unsigned int dist_to_lower = std::abs((int)lower_bound->first - (int)key);
unsigned int dist_to_upper = std::abs((int)upper_bound->first - (int)key);
return (dist_to_upper < dist_to_lower) ? upper_bound : lower_bound;
}
above is wrong. should be like this
template
typename std::map<T1, T2>::const_iterator nearest_key(const std::map<T1, T2>& map, T1 key)
{
auto lower_bound = map.lower_bound(key);
if (lower_bound == map.end()) return --lower_bound;
auto upper_bound = lower_bound; upper_bound++;
if (upper_bound == map.end()) return lower_bound;
auto dist_to_lower = lower_bound->first - key;
auto dist_to_upper = upper_bound->first - key;
return (dist_to_upper < dist_to_lower) ? upper_bound : lower_bound;
}
I had to solve the same problem, however provided answers do not give me the correct answer. Here is a full example if someone wants
template <typename T>
class Key
{
public:
T x;
T y;
explicit Key(T x_, T y_): x(x_), y(y_){}
bool operator<( const Key<T> right) const{
if((x == right.x) && (y == right.y)){
return false;
}
return true;
}
T operator-( const Key<T> right) const{
return std::sqrt(std::pow(x-right.x, 2) + std::pow(y-right.y, 2));
}
};
int main(int argc, char **argv)
{
std::map<Key<double>, double> pixel_mapper;
Key<double> k1(400,5);
Key<double> k2(4,5);
Key<double> k3(4,5);
Key<double> k4(4667,5);
Key<double> k5(1000,5);
pixel_mapper.insert(std::pair<Key<double>, double>(k2, 5));
pixel_mapper.insert(std::pair<Key<double>, double>(k3, 5));
pixel_mapper.insert(std::pair<Key<double>, double>(k4, 5));
pixel_mapper.insert(std::pair<Key<double>, double>(k1, 5));
auto it = std::min_element( pixel_mapper.begin(), pixel_mapper.end(),
[&](const auto &p1, const auto &p2)
{
return std::abs(p1.first - k5) < std::abs(p2.first - k5);
});
std::cout<< it->first.x << "," << it->first.y << std::endl;
return 0;
}
Here, we can use std:min_element to get the closest in case exact key is not present
I'am having problems while trying to iterate some maps.
Basically i have a Deposit class. Each deposit class has a multimap containing a destination Deposit and a distance. (This will be used to create a graph).
When i try to iterate all the maps i'm getting a segmentation fault error.
Here's the code:
for (int j = 0; j < deposit.size(); j++) {
for (typename multimap< Deposit<Product>*, int>::iterator it = deposit.at(j)->getConnections().begin(); it != deposit.at(j)->getConnections().end(); it++) {
cout << "From the depo. " << deposit.at(j)->getKey() << " to " << it->first->getKey() << " with the distance " << it->second << endl;
}
}
EDIT:
Deposit Class:
template<class Product>
class Deposit {
private:
multimap <Deposit<Product>*, int> connections;
public:
void addConnection(Deposit<Product>* dep, int dist);
multimap <Deposit<Product>*, int> getConnections() const;
};
(...)
template<class Product>
void Deposit<Product> ::addConnection(Deposit<Product>* depKey, int dist) {
this->connections.insert(pair<Deposit<Product>*, int>(depKey, dist));
}
template<class Product>
multimap < Deposit<Product>*, int> Deposit<Product> ::getConnections() const {
return this->connections;
}
Storage Class - This is where I populate the multimaps.
(...)
ligs = rand() % 10;
do{
ligIdx = rand() % deposit.size();
dist = rand() % 100;
deposit.at(i)->addConnection(deposit.at(ligIdx), dist);
ligs--;
}while(ligs>0);
(...)
My deposit class has 2 subclasses. I dont know why the error occurs. Is there any problem with the iterator?
Thank you very much!!!
The problem you have is pretty nasty: getConnections() returns a multimap by value.
This means that successive calls to deposit.at(j)->getConnections() refer to different temporary copies of the original multimap. Thus the the iterator created on the begin of the first temporary copy, will never match the end of the second copy, without first accessing illegally some invalid places.
Two alternatives:
if you want to iterate on a copy, make one local copy auto cnx = deposit.at(j)->getConnections(); and change your inner loop to iterate on cnx.
if you intended to iterate on the original multimap, change the signature of getConnections() to return a reference.
By the way, if you use c++11 or higher, you could consider defining the iterator in a more readable way: for (auto it = ....) or even better, using the range-for syntax as proposed by Norah Attkins in her answer.
If you have a c++11 (or 14) compiler (and you should - unless it's a work/company barrier involved) consider using range based for loops to make your code clearer
for (auto const& elem : deposit)
{
for (auto const& product : elem)
{
}
}
Apart from the stylist guidance, lacking info on what the containers actrually hold, we'd just be guessing what's wrong when answering this question. My guess is that invalid reads happen and the pointers you're accessing are not allocated (but that's a guess)