I have a map in my program that stores the code of a Product p and the amount of it.
If a ask for a new request, and if the product already exists in the map, i just need to sum the second element of the pair(code, amount) to the element in the map.
How can I do this?
void Request :: addItem (Product p, double amount) {
if(this->isItemRequest(p)) {
//p already exists in the map.
}
this->rdata.insert(pair<int, double>((int)p.getCode(), amount));
}
Thanks a lot!
Assuming your map is declared within the Request class as std::map<int, double> rdata, the code can be:
void Request::addItem( Product p, double amount )
{
if ( this->isItemRequest(p) )
{
int Code = int(p.getCode);
this->rdata[ Code ] += amount;
}
this->rdata.insert( pair<int, double>(int(p.getCode), amount) );
}
However, if isItemRequest() is just a trivial check, your code can be simplified to just:
void Request::addItem( Product p, double amount )
{
int Code = int(p.getCode);
this->rdata[ Code ] += amount;
}
P.S. Maybe, it is a good idea (if you can change the interface) to pass Product by const reference.
If you call on:
this->rdata[key] = value;
you create a value using default constructor (int() initializes to 0), return reference to it, and call operator= on it. You can avoid that by checking if key exist first:
this->rdata.count(key) != 0
or simplier
this->rdata.count(key)
if it exists you can use operatror=, operator+= and so on on reference returned by operator[]:
if (this->rdata.count(key) == 0)
this->rdata.insert( pair<int, double>( key, value ) );
else
this->rdata[key] += value;
but in this simple case
this->rdata[key] += value;
should just do.
The simplest way to do that is
rdata[p.getCode()] += amount;
If it wasn't already in the map, then [] will insert an element with value zero, so the value will end up being amount. If it was, then amount is added to the existing value.
Easy: map.find :)
#include <iostream>
#include <map>
#include <string>
typedef std::map<std::string, int> StringIntMap;
int main() {
StringIntMap map;
map["coke"] = 10;
map["fries"] = 25;
map["pizza"] = 50;
std::cout << "Before increase" << std::endl;
StringIntMap::const_iterator it;
for (it = map.begin(); it != map.end(); it++) {
std::cout << it->first << ": " << it->second << std::endl;
}
std::cout << "Now, increase pizza +50" << std::endl;
StringIntMap::iterator item = map.find("pizza");
if (item != map.end()) {
// pizza exists increase 50
item->second += 50;
} else {
std::cout << "Sorry, no pizza here" << std::endl;
}
std::cout << "after increase" << std::endl;
for (it = map.begin(); it != map.end(); it++) {
std::cout << it->first << ": " << it->second << std::endl;
}
return 0;
}
Related
I have the following simple code. I declare a vector and initialize it with one value 21 in this case. And then i am trying to find that value in the vector using find. I can see that the element "21" in this case is in the vector since i print it in the for loop. However why the iterator of find does not resolve to true?
vector<uint8_t> v = { 21 };
uint8_t valueToSearch = 21;
for (vector<uint8_t>::const_iterator i = v.begin(); i != v.end(); ++i){
cout << unsigned(*i) << ' ' << endl;
}
auto it = find(v.begin(), v.end(), valueToSearch);
if ( it != v.end() )
{
string m = "valueToSearch was found in the vector " + valueToSearch;
cout << m << endl;
}
are you sure it doesn't work?
I just tried it:
#include<iostream> // std::cout
#include<vector>
#include <algorithm>
using namespace std;
int main()
{
vector<uint8_t> v = { 21 };
uint8_t valueToSearch = 21;
for (vector<uint8_t>::const_iterator i = v.begin(); i != v.end(); ++i){
cout << unsigned(*i) << ' ' << endl;
}
auto it = find(v.begin(), v.end(), valueToSearch);
if ( it != v.end() )
{// if we hit this condition, we found the element
string error = "valueToSearch was found in the vector ";
cout << error << int(valueToSearch) << endl;
}
return 0;
}
There are two small modifications:
in the last lines inside the "if", because you cannot add directly a
number to a string:
string m = "valueToSearch was found in the vector " + valueToSearch;
and it prints:
21
valueToSearch was found in the vector 21
while it's true that you cannot add a number to a string, cout
support the insertion operator (<<) for int types, but not uint8_t,
so you need to convert it to it.
cout << error << int(valueToSearch) << endl;
This to say that the find is working correctly, and it is telling you that it found the number in the first position, and for this, it != end (end is not a valid element, but is a valid iterator that marks the end of your container.)
Try it here
My question is that of safety. I've searched cplusplus.com and cppreference.com and they seem to be lacking on iterator safety during std::move. Specifically: is it safe to call std::unordered_map::erase(iterator) with an iterator whose object has been moved? Sample code:
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
class A {
public:
A() : name("default ctored"), value(-1) {}
A(const std::string& name, int value) : name(name), value(value) { }
std::string name;
int value;
};
typedef std::shared_ptr<const A> ConstAPtr;
int main(int argc, char **argv) {
// containers keyed by shared_ptr are keyed by the raw pointer address
std::unordered_map<ConstAPtr, int> valued_objects;
for ( int i = 0; i < 10; ++i ) {
// creates 5 objects named "name 0", and 5 named "name 1"
std::string name("name ");
name += std::to_string(i % 2);
valued_objects[std::make_shared<A>(std::move(name), i)] = i * 5;
}
// Later somewhere else we need to transform the map to be keyed differently
// while retaining the values for each object
typedef std::pair<ConstAPtr, int> ObjValue;
std::unordered_map<std::string, std::vector<ObjValue> > named_objects;
std::cout << "moving..." << std::endl;
// No increment since we're using .erase() and don't want to skip objects.
for ( auto it = valued_objects.begin(); it != valued_objects.end(); ) {
std::cout << it->first->name << "\t" << it->first.value << "\t" << it->second << std::endl;
// Get named_vec.
std::vector<ObjValue>& v = named_objects[it->first->name];
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
// And then... is this also safe???
it = valued_objects.erase(it);
}
std::cout << "checking... " << named_objects.size() << std::endl;
for ( auto it = named_objects.begin(); it != named_objects.end(); ++it ) {
std::cout << it->first << " (" << it->second.size() << ")" << std::endl;
for ( auto pair : it->second ) {
std::cout << "\t" << pair.first->name << "\t" << pair.first->value << "\t" << pair.second << std::endl;
}
}
std::cout << "double check... " << valued_objects.size() << std::endl;
for ( auto it : valued_objects ) {
std::cout << it.first->name << " (" << it.second << ")" << std::endl;
}
return 0;
}
The reason I ask is that it strikes me that moving the pair from the unordered_map's iterator may (?) therefore *re*move the iterator's stored key value and therefore invalidate its hash; therefore any operations on it afterward could result in undefined behavior. Unless that's not so?
I do think it's worth noting that the above appears to successfully work as intended in GCC 4.8.2 so I'm looking to see if I missed documentation supporting or explicitly not supporting the behavior.
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
Yes, it is safe, because this doesn't actually modify the key. It cannot, because the key is const. The type of *it is std::pair<const ConstAPtr, int>. When it is moved, the first member (the const ConstAPtr) is not actually moved. It is converted to an r-value by std::move, and becomes const ConstAPtr&&. But that doesn't match the move constructor, which expects a non-const ConstAPtr&&. So the copy constructor is called instead.
I'm trying to iterate through a map to read out a string and then all of the numbers in a vector to a file. I copied and pasted the typedef line, then adjusted it to my code, so I'm not positive it's correct. Anyways, Visual Studio is giving me errors on the use of iterator_variable in my loops. It says type name is not allowed. How can I fix this?
ofstream output("output.txt");
typedef map<string, vector<int>>::iterator iterator_variable;
for (iterator_variable iterator = misspelled_words.begin(); iterator != misspelled_words.end(); iterator++)
{
output << iterator_variable->first;
for (int i = 0; i < misspelled_words.size(); i++)
{
output << " " << iterator_variable->second[i];
}
output << endl;
}
You should access the iterator like iterator->first instead of iterator_variable->first.
And for the inner loop, you probably want to iterate through 0 to iterator->second.size() instead of misspelled_words.size().
ofstream output("output.txt");
typedef map<string, vector<int>>::iterator iterator_variable;
for (iterator_variable iterator = misspelled_words.begin(); iterator != misspelled_words.end(); iterator++)
{
output << iterator->first;
for (int i = 0; i < iterator->second.size(); i++)
{
output << " " << iterator->second[i];
}
output << endl;
}
You can use the the new range based for loop and auto for more concise and readable code too.
ofstream output("output.txt");
for ( auto const & ref: misspelled_words ) {
output << ref.first;
for (auto const & ref2 : ref.second ) {
output << " " << ref2;
}
output << "\n"; // endl force a stream flush and slow down things.
}
I'm trying to write a copy constructor for an object managing a STL map containing pointers, where the key is a string. However, when I attempt to insert new values in the map, the pointers are set to NULL:
// ...
for(std::map<std::string, data_base*, order>::const_iterator it = other.elements.begin();
it != other.elements.end(); ++it){
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
elements[it->first] = t;
std::cout << "INSERTED: " << std::hex << elements[it->first] << std::endl;
}
// ...
other is the object being copied and elements the map. The clone() method returns a pointer to a new object (via new).
Running the code above I get something like:
CLONE: 0xcfbbc0
INSERTED: 0
I'm not a very experienced programmer and this issue is probably simple to fix, but I didnt find any solution to it searching around.
Thanks a lot for your time.
I don't see any problem with this code, other than maybe
std::map<std::string, data_base*, order>::const_iterator it
Here order gives the key comparator to use to sort the pairs contained in the map (often implemented as a tree).
Maybe you're doing something wrong in it, making your [] operator don't find the right ke, making your last line logging a new pair with a null ptr.
First, try without that order, using the default key-comparator (std::less), then if it don't work, post your order definition and the map declaration. If it's not enough, just provide a simple complete program that reproduce the problem.
I just wrote a simple similar test, using the default key-comparator :
#include <map>
#include <string>
#include <iostream>
struct Data
{
int k;
Data* clone() { return new Data(); }
};
typedef std::map< std::string, Data* > DataMap;
DataMap data_map;
int main()
{
data_map[ "hello" ] = new Data();
data_map[ "world" ] = new Data();
DataMap other_map;
for( DataMap::const_iterator it = data_map.begin(); it != data_map.end(); ++it)
{
Data*t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
other_map[it->first] = t;
std::cout << "INSERTED: " << std::hex << other_map[it->first] << std::endl;
}
std::cin.ignore();
return 0;
}
On VS2010SP1, this outputs :
CLONE: 00034DD0
INSERTED: 00034DD0
CLONE: 00035098
INSERTED: 00035098
So it should be the problem, or maybe you're doing something wrong before.
Try this out, to help debug the issue. I'd recommend double-checking that the order function is correct. You can remove it to use std::less<T>, which is known to work.
// ...
typedef std::map<std::string, data_base*, order> string_db_map;
for(string_db_map::const_iterator it = other.elements.begin();
it != other.elements.end();
++it)
{
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
std::pair<string_db_map::iterator, bool) result = elements.insert(
string_db_map::value_type( it->first, t));
if ( !result.second )
{
std::cout << "element['" << it->first << "'] was already present, and replaced." << std::endl;
}
std::coud << "INSERTED [iterator]: " << std::hex << (*result.first).second << std::endl;
std::cout << "INSERTED [indexed]: " << std::hex << elements[it->first] << std::endl;
}
// ...
I have my std::map defined as
typedef std::map<string,ImageData*> ImageDataMap;
typedef std::pair<string,ImageData*> ImageDataPair;
typedef std::map<string,ImageData*>::iterator ImageDataIterator;
The above map stores string which is an image file name and ImageData which is the the image metadata. When i use the find as shown below
ImageDataIterator iter = imageMap->find("Fader.tga");
if(iter == imageMap->end()){...}
The iter->first is a badptr and so it fails the if condition below is. What's wrong here? Running on vc9 express edition on xp64 (the program is 32bit)
An iterator returned as map::end by map::find() means that the specified key was not found in the container. You cannot dereference it to access its elements. It will crash your application.
EDIT:
Let's be clear. The problem is that you are inverting the logic, ok? You can only use an iterator if it's valid, therefore iter must be different from map::end. This means that map::find() was successful and found the element you were looking for:
if (iter != imageMap->end())
{
// element FOUND! Use it!
cout << iter->first << endl;
}
else
{
// Not found! Can't use it.
}
Your mistake is the if comparison you're currently doing: if (iter == imageMap->end()) which means execute the following block of code if the element I searched for is not in the map. That's why when iter->first is executed the application breaks.
#include <iostream>
#include <map>
#include <string>
typedef int ImageData;
typedef std::map<std::string,ImageData*> ImageDataMap;
typedef std::map<std::string,ImageData*>::iterator ImageDataIterator;
using namespace std;
int main()
{
ImageDataMap mymap;
int value_1 = 10;
int value_2 = 20;
int value_3 = 30;
mymap["a"] = &value_1;
mymap["b"] = &value_2;
mymap["c"] = &value_3;
// Search/print valid element
ImageDataIterator it = mymap.find("a");
if (it != mymap.end()) // will execute the block if it finds "a"
{
cout << it->first << " ==> " << *(it->second) << endl;
}
// Searching for invalid element
it = mymap.find("d"); // // will only execute the block if it doesn't find "d"
if (it == mymap.end())
{
cout << "!!! Not found !!!" << endl;
cout << "This statement will crash the app" << it->first << endl;;
}
cout << "Bye bye" << endl;
return 0;
}
Perhapes you should change if(iter == imageMap->end()){...} to if(iter != imageMap->end()){...}