Adding elements into a c++ map - c++

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

C++ finding uint8_t in vector<uint8_t>

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

Moving objects from one unordered_map to another container

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.

C++: Iterate Through Map

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.
}

C++ STL map with custom comparator storing null pointers

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;
}
// ...

std:map iterator returns badptr on find

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()){...}