I have an STL map that maps a key to a vector of deliverables.
I would like to search the map for a certain key and print out all of the deliverables. I am trying to iterate through the vector and call print on each item.
typedef std::vector<Deliverables> MyDeliverables;
typedef std::map<int, MyDeliverables> MyMap;
MyMap map1;
template < class T >
void printVector( const vector <T> &v)
{
for (auto p = v.begin(); p != v.end(); ++p)
*p->print();
}
int main()
{
Deliverables del("Name", 12, 12, 2018);
map1.insert(MyMap::value_type(1, MyDeliverables()));
auto search = map1.find(1);
if (search != map1.end()) {
std::cout << "Found Student ID: " << search->first << '\n';
printVector(search->second);
}
else {
std::cout << "Not found\n";
}
}
Error C2662 'void Deliverables::print(void)': cannot convert 'this' pointer from 'const Deliverables' to 'Deliverables &'
Line: *p->print();
How can I correctly print the deliverables?
The issue is with the code you are not showing:
Deliverables::print()
It is not const, so you cannot use it. Declare the print function as const, and then you can use a const Deliverables&:
Deliverables::print() const
Then change your loop to avoid confusion as to what to dereference and how many times:
for(const auto& p: v)
p.print();
Related
I am trying to count the most common words in text, so firstly i am filling the map of <word, count>, secondly I am trying to move the contents of the map <word, count> to the multimap<count, word>. But there is the problem: node type of STL maps/hashes are <const Key, Value>. I have tried to const_cast, it worked, but it seems ugly and UB'ly. Are there any another ways to move the contents?
int main()
{
std::unordered_map<std::string, std::size_t> words;
std::multimap<std::size_t, std::string> sorted_ordered_words;
std::for_each(std::istream_iterator<std::string>{std::cin}, {}, [&](const std::string& str)
{
++words[str];
});
std::transform(std::make_move_iterator(std::begin(words)),
std::make_move_iterator(std::end(words)),
std::inserter(sorted_ordered_words, std::end(sorted_ordered_words)),
[](decltype (words)::value_type && v)
{
return std::make_pair(v.second, std::move(const_cast<std::string&>(v.first)));
});
for (const auto &[count, word] : sorted_ordered_words)
{
std::cout << count << " - " << word << std::endl;
}
return 0;
}
A well defined way to move the string from the original container is to extract the node it is in first. That exposes a non-const reference to the key.
for (auto it = words.begin(); it != words.end();) {
auto copy = it;
++it;
auto node = words.extract(copy);
static_assert(std::is_same_v<decltype(std::move(node.key())), std::string&&>);
sorted_ordered_words.emplace(node.mapped(), std::move(node.key()));
}
Using const_cast is definitely UB because the keys are just moved from but are still in the map, violating internal constraints (all strings are the same, "", when they should all be unique). This could lead to crashes in the destructor for example.
If it's just for printing in order, you don't need to build a multimap. It's faster to just sort a vector of pairs rather than inserting into a multimap one by one.
std::vector<decltype(words)::value_type*> vec;
vec.reserve(words.size());
std::transform(words.begin(), words.end(), std::back_inserter(vec), [](auto& v) { return &v; });
std::sort(vec.begin(), vec.end(), [](auto* l, auto* r) {
return l->second < r->second;
});
for (const auto* item : vec) {
const auto& [word, count] = *item;
std::cout << count << " - " << word << std::endl;
}
You could also use std::vector<std::pair<std::size_t, const std::string&>> and sort by .first.
In c++ I need to search a vector containing a pair, in reverse, by the string. I cannot use a map because the strings are not unique and order is important. I then want to return a forward iterator if the string is found or the end iterator if the string is not found.
Please see below for my current code. I have no problem when the string is found but, if the string is not found, I get a segfault in the conditional statement in main.
vector<pair<string, int>>::iterator prev_it(const string& pred,
vector<pair<string, int>> prevpreds) {
vector<pair<string, int>>::reverse_iterator rit;
for(rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit) {
if (rit->first == pred) {
return (rit+1).base();}
}
if(rit == prevpreds.rend()) {
return prevpreds.end();
}
}
and in main:
int main() {
vector<pair<string, int>> test;
for(int i = 0; i <= 5; ++i) {
pair<string, int> mypair;
mypair = make_pair("X"+to_string(i%4+1), i+1);
test.emplace_back(mypair);
}
string tpred = "X"+to_string(6);
vector<pair<string, int>>::iterator tit;
tit = prev_it(tpred, test);
if (tit != test.end()) {
cout << tit->first << " " << tit->second << endl;
}
else {cout << "This is the end." << endl;}
}
The code works if tpred is one of X1 to X4. If tpred is X6 (i.e. not an element of test) then I get a segfault. What I would like to be able to do is return the end forward iterator and then, as in main(), have a conditional based on this.
Edit: I am new to c++ (about a year). I am returning a forward iterator because I need to use the iterator later and this seems clearer (but I could be wrong). As far as I understand, a multimap allows non-unique keys but will order the unique keys. I should have been clearer and said time order was important, not key order. I prefer not to use auto while developing because I like to see what I container element/iterator I am using, but point taken.
You're using an iterator of a destructed object. Pass prevpreds by reference, so the iterator maintains valid.
vector<pair<string, int>>::const_iterator prev_it(const string& pred,
const vector<pair<string, int>> &prevpreds)
{
vector<pair<string, int>>::const_reverse_iterator rit;
for (rit = prevpreds.rbegin();
rit != prevpreds.rend(); ++rit)
{
if (rit->first == pred)
{
return (rit + 1).base();
}
}
return prevpreds.end();
}
int main()
{
// ...
vector<pair<string, int>>::const_iterator tit; // <-- uses const iterator
tit = prev_it(tpred, test);
// ...
}
I have template class which has a vector and default value in it
T _default;
vector<T> Vals;
I want to iterate over it and print the values based on type of default (either string/int/double).
so I wrote
if (typeid(int) == typeid(_default))
{
for each(auto itr in Vals)
{
//logic to process each element in vector
int temp;
//temp = *itr; <------ how to convert from iterator to the value (value held in vector)
}
}
else if (typeid(string) == typeid(_default))
{
for each(auto itr in Vals)
{ //logic to process each element in vector
string temp;
//temp = *itr; <------ how to convert from iterator to the value (value held in vector)
}
}
else if (typeid(double) == typeid(_default))
{
for each(auto itr in Vals)
{
//logic to process each element in vector
double temp;
//temp = *itr; <------ how to convert from iterator to the value (value held in vector)
}
}
How to convert iterator to corresponding int/string/double. When I dereference it using *itr gives me error C2100: illegal indirection error.
UPDATE:
Sorry for update - but I actually want to compare value of a string say string validateStr to the values contained in vector<T> Vals. So I need to know the type of the values stored in Vals and then compare string by either doing (string to int)/(string to double)/(string(as is)) to each of the value in Vals. So basically this code will check if the _default value is actually is any of the values present in allowed Vals.
In a range loop, the "iteration variable" has the type of the elements.
It's not an iterator.
You can't write the function like this since the entire function must be type-correct regardless of what T is.
What you can do is use an overloaded (or templated) function and pass the value to that:
void print(int i) { cout << "int " << i; }
void print(string s) { cout << "string " << s; }
// ...
for (auto v: Vals)
{
print(v);
}
Seems like you're missing the point of a template. The idea is that you can create generalized code that handles all cases. So for example you say you want to:
I want to iterate over it and print the values based on type of _default (either string/int/double)
In that case I'd say that you should create a print method in your class. For example:
void print() { for(const T& i : Vals) cout << i << ' '; }
Live Example
Firstly, the whole point of having a template is to avoid what you're trying to do with your class.
Apart from the fact that you can achieve same results with specialized templates why not try auto temp = *itr; or better still type cast(*itr) but i'll still advise trying out template specialization.
A little example snippet for template specialization would be
template<typename T>
T add(T v1, T v2){
return v1 + v2;
}
template <> double add(double v1, double v2){ return v1 + v2; }
template <> std::string add(std::string v1, std::string v2){
return v1 + v2;
}
template <> const char* add(const char* v1, const char* v2){
return add(std::string(v1),std::string(v2)).c_str();
}
int main(){
std::cout << add(1, 3) << std::endl;
std::cout << add(2.6, 4.0) << std::endl;
std::cout << add("str", "ing") << std::endl;
}
I have a map input which contains a list of words and their counts.
I use this function to print the map input:
template <class KTy, class Ty>
void PrintMap(map<KTy, Ty> map)
{
typedef std::map<KTy, Ty>::iterator iterator;
for (iterator p = map.begin(); p != map.end(); p++)
cout << p->first << ": " << p->second << endl;
}
it prints the values likes this:
you : 296
she : 14
go : 29
how can I print it in descending order of word count.
Try the following:
// Copy it into a vector.
std::vector<std::pair<std::string,int>> vector( map.begin(), map.end() );
// Sort the vector according to the word count in descending order.
std::sort( vector.begin(), vector.end(),
[]( const auto & lhs, const auto & rhs )
{ return lhs.second > rhs.second; } );
// Print out the vector.
for ( const auto & item : vector )
std::cout << item.first << ": " << item.second << std::endl;
The map is sorted according to the keys, i. e. alphabetically. You cannot change that behaviour. Therefore, the easiest way to get the job done is copying it into a vector and sorting it with a user defined compare function.
map stores elements sorted by key not by value. Unless you want to change the type of your map, the only way to do it is to sort your elements after getting them out. I think the easiest way to do so is via std::sort
Given that dereferencing an iterator into a map gives a const value_type&, we can take advantage of the reference to avoid actually creating copies of value_type (which is a std::pair<Key, Value>). However, the code will be a little longer than if we wanted to copy:
template <class KTy, class Ty>
void PrintMap(const std::map<KTy, Ty>& map)
{
using vt = const typename std::map<KTy, Ty>::value_type*;
std::vector<vt> vec(map.size());
size_t i = 0;
for(const auto& keyval : map)
{
vec[i++] = &keyval;
}
std::sort(std::begin(vec), std::end(vec), [](vt _lhs, vt _rhs){return _lhs->second > _rhs->second;});
for(const auto& el : vec)
std::cout << el->first << ": " << el->second << std::endl;
}
With a test
std::map<std::string, int> myMap{{"you", 296}, {"she", 14}, {"go", 29}};
PrintMap(myMap);
Outputs
you: 296
go: 29
she: 14
This should be much faster if your map is of non-trivially copyable elements.
Put data in another container, sort by word count, print.
I have a function already that takes out the key value with the most mapped value.
// Function for finding the occurances of colors or in this case hex values
void findOccurrances(double * mostNumTimes, map<string, int> &hexmap, string * colorlist)
{
map<string,int>::iterator it = hexmap.begin();
for( ;it != hexmap.end(); it ++)
{
if(*mostNumTimes <= it->second)
{
*mostNumTimes = it->second;
*colorlist = it->first;
}
}
}
Is there an easy way to expand it to show the top five results?
I know you can copy it to a vector and what not but I'm wanting an easier way of doing it.
Copying into a vector isn't that difficult:
typedef std::pair<string, int> Pair;
std::vector<Pair> contents(hexmap.begin(), hexmap.end());
Done.
But to find the top 5, believe it or not <algorithm> has a function template that does exactly what you want. In C++11, which usefully has lambdas:
std::vector<Pair> results(5);
std::partial_sort_copy(
hexmap.begin(), hexmap.end(),
results.begin(), results.end(),
[](const Pair &lhs, const Pair &rhs) { return lhs.second > rhs.second; }
);
results now contains the top 5 entries in descending order.
I have a little bit confused about the arguments. why you write the function in this way, And I try for this.
string colorlist;
double max_val = 0;
for (int i = 0; i < 5; ++i)
{
findOccurrances(&max_val, hexmap, &colorlist);
cout << colorlist << " " << max_val << endl; // to do something
mapStudent.erase(colorlist);
}
I'd interchange the key and value and create new map
std::map<int,std::string> dst;
std::transform(hexmap.begin(), hexmap.end(),
std::inserter(dst, dst.begin()),
[](const std::pair<std::string,int> &p )
{
return std::pair<int,std::string>(p.second, p.first);
}
);
And now print top five values of dst in usual way,
typedef std::map<int, std::string> Mymap;
Mymap::iterator st = dst.begin(), it;
size_t count = 5;
for(it = st; ( it != dst.end() ) && ( --count ); ++it)
std::cout << it->second << it->first <<std::endl ;
Edit:
Use a std::multimap if there are same int (value) for more than one std::string (key) in your hexmap