Updating std::map object data - c++

I have a std::map object as follows
typedef std::map<int,int> RoutingTable;
RoutingTable rtable;
and then I've initialized it in a function
for (int i=0; i<getNumNodes(); i++)
{
int gateIndex = parentModuleGate->getIndex();
int address = topo->getNode(i)->getModule()->par("address");
rtable[address] = gateIndex;
}
Now I want to change the values in the rtable in another function. how can I achieve this?

Pass the rtable by reference:
void some_func(std::map<int, int>& a_rtable)
{
// Either iterate over each entry in the map and
// perform some modification to its values.
for (std::map<int, int>::iterator i = a_rtable.begin();
i != a_rtable.end();
i++)
{
i->second = ...;
}
// Or just directly access the necessary values for
// modification.
a_rtable[0] = ...; // Note this would add entry for '0' if one
// did not exist so you could use
// std::map::find() (or similar) to avoid new
// entry creation.
std::map<int, int>::iterator i = a_rtable.find(5);
if (i != a_rtable.end())
{
i->second = ...;
}
}

Related

What's the problem of saving pointer of vector inside std::map

I am trying to find the best sum with memorization, but when saving the vector pointer inside a map the values keep appending inside the vector and getting the wrong vector.
if I commented out the map insertion it works properly.
saving nullptr is not possible in case trying to save vector inside the map by reference.
std::vector<int> *bestSumV(int target, int nums[], int size) {
static std::map<int, std::vector<int> *> memo;
if (memo.find(target) != memo.end())
return memo.at(target);
if (target == 0)
return new std::vector<int>();
if (target < 0)
return NULL;
std::vector<int> *bestCom = nullptr;
for (int i = 0; i < size; i++) {
int reminder = target - nums[i];
std::vector<int> *reminderResult = bestSumV(reminder, nums, size);
if (reminderResult != NULL) {
reminderResult->push_back(nums[i]);
if (bestCom == nullptr || reminderResult->size() < bestCom->size()) {
bestCom = static_cast<std::vector<int> *>(reminderResult);
}
}
}
// if i commented out the map insertion i am getting the correct value
// and getting a vector of 5 items
memo.insert(std::make_pair(target, std::move(bestCom)));
return bestCom;
}
void runHowbestTest() {
int testArray[] = {5, 4, 2};
std::vector<int> *bestSum25 = bestSumV(25, testArray, 3);
for (int i = 0; i < bestSum25->size(); i++) {
std::cout << "the items " << bestSum25->at(i) << std::endl;
}
}
bestCom is a std::vector<int> * don't std::move it. It's pointless, and makes the code hard to read.
reminderResult is already a std::vector<int> *, no need to static_cast<std::vector<int> *> it.
On this line
memo.insert(std::make_pair(target, std::move(bestCom)));
bestCom might be null. When that happens,
if (memo.find(target) != memo.end())
return memo.at(target);
will return and bypass the function logic. You need:
if (memo.find(target) != memo.end() && memo.at(target))
return memo.at(target);
And, most likely, the real issue:
// returns a memoized value
std::vector<int> *reminderResult = bestSumV(reminder, nums, size);
...
// modifies it.
reminderResult->push_back(nums[i]);
You cannot modify a memoized value and expect it to be valid.
Using objects instead of pointers fixes the issue: https://godbolt.org/z/v35oT4 . But I make no claims about what it does to performance.

How to remove the vector object from the map

I have a map std::map<int, std::vector<Data>>myMap and my structure is defined as follows.
struct Data
{
int x;
int z;
int y;
};
in myMap key is int and value is vector of structure
I'm given with x and key value.
I want remove the object from the vector where the key and x value of the vector object matches
I have tried by giving all the x,y,z values of the structure :
myMap[123].erase(std::remove(myMap[123].begin(), myMap[123].end(), {1,2,3}), myMap[priority].end());
But Here only X value will be given.
I have tried to write a function as follows :
void deleteByx(int x)
{
for (std::map<int,std::vector<Data>>::iterator it = myMap.begin(); it != myMap.end(); it++)
{
std::vector<Data> list = it->second;
for (std::vector<Data>::iterator vec_it = list.begin(); vec_it != list.end(); vec_it++)
for(int index = 0;index < list.size();index++)
{
if (vec_it->x == x)
{
list.erase(vec_it);
}
}
}
}
but here the function deletes the element from the local vector list
If you want to have access to the actual data item, then you need to get a reference to that item. Instead, your current code gets a copy of the vector.
Thus this line:
std::vector<Data> list = it->second;
Should be:
std::vector<Data>& list = it->second;
Thanks for the time. I have tried with the following code and it worked for me.
for ( itr = myMap[key].begin(); itr != myMap[key].end(); ++itr)
{
if (itr->x == 1)
{
myMap[key].erase(itr);
break;
}
}

Use existing object in vector or create new if not exists in C++

Let's assume that I have a class named Store which contains products. Functions are inlined for simplicity.
class Store
{
public:
Store(string name)
: _name(name)
{}
string getName() const
{ return _name; };
const std::vector<string> getProducts()
{ return _products; };
void addProduct(const string& product)
{ _products.push_back(product); }
private:
const string _name;
std::vector<string> _products;
};
Then I have a two dimensional string array which contains store-product -pairs. Same store can be multiple times in array.
string storeListing[4][2] = {{"Lidl", "Meat"},
{"Walmart", "Milk"},
{"Lidl", "Milk"},
{"Walmart", "Biscuits"}};
Now I want to iterate through array, create Store-object for each store in array and add products of it to object. So I need to use existing Store-object or create a new if there is no any with correct name yet. What is a way to implement this? Currently I'm trying to use pointer and set it to relevant object, but I'm getting sometimes segmentation faults and sometimes other nasty problems when I modify code slightly. I guess I'm calling some undefined behavior here.
std::vector<Store> stores;
for (int i = 0; i < 4; ++i) {
string storeName = storeListing[i][0];
string productName = storeListing[i][1];
Store* storePtr = nullptr;
for (Store& store : stores) {
if (store.getName() == storeName) {
storePtr = &store;
}
}
if (storePtr == nullptr) {
Store newStore(storeName);
stores.push_back(newStore);
storePtr = &newStore;
}
storePtr->addProduct(productName);
}
Most likely, because you insert "Store" copies into your vector:
if (storePtr == nullptr) {
Store newStore(storeName); //create Store on stack
stores.push_back(newStore); //Make a COPY that is inserted into the vec
storePtr = &newStore; // And this is where it all goes wrong.
}
newStore goes out of scope at the end of the if and StorePtr is lost.
Try it with:
storePtr = stores.back();
Or make your vector a std::vector<Store*>.
And then:
if (storePtr == nullptr) {
Store * newStore = new Store(storeName); //create Store on stack
stores.push_back(newStore); //Make a COPY that is inserted into the vec
storePtr = newStore; // And this is where it all goes wrong.
}
And of course, as the comments suggest, a std::map would be better suited here.
In short, std::map stores key-value pairs. The key would most likely be your store name, and the value the product.
Quick example:
std::map<std::string, std::string> myMap;
myMap["Lidl"] = "Milk";
myMap["Billa"] = "Butter";
//check if store is in map:
if(myMap.find("Billa") != myMap.end())
....
Note, you can of course use your Store object as value. To use it as key, you have to take care of a few things:
std::maps with user-defined types as key
For your specific example i would suggest you use a std::string as key, and a vector of Products as value.
Use a std::unordered_set<Store>, where the hash type is the string name of the store. Using a map-like type would lead to duplicated storage of the store name (one time as a key to the map and one time inside the Store object itself).
template <>
struct std::hash<Store> {
using Store = argument_type;
using result_type = std::size_t;
result_type operator()(const argument_type& s) const noexcept {
return result_type{ std::hash<std::string>{}(s._name) }();
}
};
std::unordered_set<Store> stores;
for (int i = 0; i < 4; ++i) {
string storeName = storeListing[i][0];
string productName = storeListing[i][1];
auto iter = stores.find(storeName);
if(iter == stores.end()) iter = stores.emplace(storeName);
iter->addProduct(productName);
}
There are a few problems in your approach.
Problem 1:
Store has a const data member. This will make it impossible to reorder the vector of stores. That needs to be corrected.
Problem 2:
You need to point at the right Store after insertion. Here's one approach:
// decompose the problem:
// first step - get a pointer (iterator) to a mutable store *in the vector*
auto locate_or_new(std::vector<Store>& stores, std::string const& storeName)
-> std::vector<Store>::iterator
{
auto iter = std::find_if(begin(stores), end(stores),
[&](Store& store)
{
return store.getName() == storeName;
});
if (iter == end(stores))
{
iter = stores.emplace(end(stores), storeName);
}
return iter;
}
//
// 2 - insert the product in terms of the above function.
auto addProduct(std::vector<Store>& stores, std::string const& storeName, std::string const& productName)
-> std::vector<Store>::iterator
{
auto istore = locate_or_new(stores, storeName);
istore->addProduct(productName);
return istore;
}
Note:
Since inserting objects into a vector can cause iterator invalidation, you will need to be careful to not hold references to objects inside the vector across sections of code that could create new stores.
if (storePtr == nullptr) {
Store newStore(storeName);
stores.push_back(newStore);
storePtr = &newStore;
}
Once the if ends newStore is gone and you are left with dangling pointer storePtr.
You could use an std::set<Store *> here
std::set<Store *> myset;
Store *c = new Store("store3");
std::set<Store *>::iterator iter = myset.find(c);
if(iter!=myset.end())
{
(*iter)->addProduct("Product1");
}
else
{
c->addProduct("Product1");
myset.insert(c);
}
There is a solution using vectors and iterators. Dont forget to include the "algorithm" header!
std::vector<Store> stores;
for (int i = 0; i < 4; ++i) {
string storeName = storeListing[i][0];
string productName = storeListing[i][1];
auto storeIter = std::find_if(stores.begin(), stores.end(), [storeName](Store store) -> bool {
return store.getName() == storeName;
}); //Find the store in the vector
if (storeIter == stores.end()) //If the store doesn't exists
{
stores.push_back(Store(storeName)); //Add the store to the vector
storeIter = prev(stores.end()); //Get the last element from the vector
}
Store* storePtr = &(*storeIter); //You can convert the iterator into a pointer if you really need it
storeIter->addProduct(productName);
//storePtr->addProduct(productName);
}

Changing an element within a vector of structures

I'm iterating through a set of elements within a vector of structures and want to change an element in one of the structures. When I write to the element to change the value, the update isn't retained. Here is what I have:
first, in a header file:
std::vector<Sched::ScheduledEvent_t> v_SchedEvents;
typedef std::vector<Sched::ScheduledEvent_t>::const_iterator event_iter;
then later in a .cpp module...
for (event_iter i = v_SchedEvents.begin(); i != v_SchedEvents.end(); ++i)
{
ScheduledEvent_t event = *i;
if(event.member == true) {
event.member = false;
}
}
The value of event.member for the given structure in the vector isn't staying false. When returning to this loop, the conditional statement is run again.
Could it have anything to do with the typedef for the iterator?
Two problems here.
1) You're making a copy:
ScheduledEvent_t event = *i;
event is a copy of the element in the vector. Modifing event won't affect it.
2) You are using a const_iterator which only allows reading the value, not changing it.
Use a iterator instead
typedef std::vector<Sched::ScheduledEvent_t>::iterator event_iter;
and use it directly:
if (i->member) { // == true useless
i->member = false;
}
Or a for-range loop if you have access to C++11 or more recent:
for (auto & event : v_SchedEvents) {
if (event.member) {
event.member = false;
}
}
The const_iterator prevents the modification of the referenced value.
iterator : Behaves like value_type*.
const_iterator : Behaves like const_value_type*
vector<node> v;
v.push_back(node(10, 11));
for( std::vector<node>::const_iterator it = v.begin() ; it != v.end() ; ++it ){
node n = *it;
n.member = 12; //A local copy of node `*it`. So its ok to modify.
n = node(10, 13); //Okay since local Copy is not const
//(*it).member = 12; //error assignment of member 'node::a' in read-only object
//*it = node(10, 13); //error passing 'const node' as 'this' argument discards qualifiers
}
for( std::vector<node>::iterator it = v.begin() ; it != v.end() ; ++it ){
//cout << *it.a;
node n = *it;
n.a = 12;
n = node(10, 13); //No problem
(*it).a = 12; //No problem
*it = node(10, 13); //No problem
}
You are able to modify member value because you are getting a local copy of const_iterator referenced value by doing ScheduledEvent_t event = *i;
It is a simple matter of reference type and local variable.
When You do
ScheduledEvent_t event = *i;
You are creating a simple local variable "event" and copying everything from the source where "i" is pointing to.
So, if you wish to change the element in the structure change your code from
event.member = false;
to
*i.member = false;
or (my preference)
i->member = false;
I hope that helps.

Copying C++ Map into key and value vectors

I have a map and I want the first column i.e (*it).first to be pushed back into a vector then (*it)->second to be pushed back into another vector
Is this the best way to do it?
std::vector<std::string>test;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
}
My other question is if i have a loop i.e
how would I insert all the integers i into (*it).first?
for(int i = 0; i < 10; i++)
{
// 1 - 10 will go in (*it).first
}
I want to have some integers in (*it).first and have associated values in (*it).second;
Use std::transform.
First define two functions key and value which take the pair of strings and return the first or second value, respectively.
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
Then use std::transform from <algorithm> with the functions to transform the map into either a vector of keys or a vector of values.
int main()
{
using namespace std; // be explicit normally, trying to be brief here
map<string, string> contacts;
contacts["alice"] = "555-2701";
contacts["bob"] = "555-2702";
vector<string> keys(contacts.size());
vector<string> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
cout << "Keys:\n";
copy(keys.begin(), keys.end(), ostream_iterator<string>(cout, "\n"));
cout << "\n";
cout << "Values:\n";
copy(values.begin(), values.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
Output:
Keys:
alice
bob
Values:
555-2701
555-2702
Your first question, "how can I push the first column of my map into one vector and the 2nd column into another" is solved thus:
std::map<std::string, std::string> mymap;
std::vector<std::string> keys;
std::vector<std::string> values;
for ( std::map<std::string,std::string>::iterator it=mymap.begin() ; it != mymap.end(); ++it )
{
keys.push_back(it->first);
values.push_back(it->second);
}
Your second question, "how would insert all the integers i into (*it).first ?" is solved thus:
std::map<int, int> mymap2;
for(int i = 0; i < 10; i++)
{
// Insert default value into map
// This sets '(*it).first' to 'i' and
// '(*it).second' to a default value (in
// this case, 0).
mymap2[i];
}
or
std::map<int, int> mymap3;
for(int i = 0; i < 10; i++)
{
// Insert specified value into map
// this sets '(*it).first' to 'i', and
// '(*it).second' to the value returned from the function.
maymap3[i] = ChooseSpecificValue(i);
}
Well, it can be done with a simple loop:
for (auto const& p: mymap) {
vec1.push_back(p.first);
vec2.push_back(p.second);
}
Or using the std::transform algorithm, though it's quite verbose here:
std::transform(mymap.begin(), mymap.end(), std::back_inserter(vec1),
[](MyMap::const_reference p) { return p.first; });
Assuming you've declared your map as string key and value (ie map<string, string> mymap; then it would be like below, also assuming you've declare 'it' variable as map<string, string>::iterator it, etc:
std::vector<std::string> test;
std::vector<std::string> second;
std::map<string, string>::iterator it;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
second.push_back((*it).second);
}
Not sure about your next question.
The first part of your question:
std::vector<std::string> test;
std::vector<std::string> test2; // assuming map is from string to string
for (it = mymap.begin(); it != mymap.end(); ++it)
{
test.push_back(it->first); // push first in one vector
test2.push_back(it->second); // push second in another vector
}
So, yes a simple for can do what you want.
The second part of your question:
Since you are updating the key of the map, you would need to remove it from the map and insert the changed one. So:
std::string first, second;
first = it->first;
second = it->second;
mymap.erase(it); // be careful with invalidating iterator
// change first
mymap[first] = second;
To change first by adding all integers i to it, that would really depend on the type of first. For example with a string, you may mean something like this:
ostringstream sout;
for (int i = 0; i < 10; ++i)
sout << (i?" ":"") << i;
first = sout.str();
Or if first is for example a set, you may mean something like this:
for (int i = 0; i < 10; ++i)
first.insert(i);
and my other question is if i have a loop i.e how would insert all the
integers i into (*it).first?
In the case of a std::map, you can't modify the iterator returned like that ... the key member (i.e., the first) in the std::map key/value pair data-structure is intentionally designated as a constant value, and is initialized to its constant value at the beginning of the key/value pair's lifetime in the std::map data-structure. If the keys weren't constant, you would end up creating havoc when you change the key, since the nodes in a std::map are suppose to be sorted by the keys. The second member of the key/value pair data-structure is the member that can be changed.
So if you want to insert a set of key/value pairs in a map, you could simply do the following:
std::map<int, int> mymap;
int some_other_value = 100;
for (int i=0; i < 10; i++)
{
mymap[i] = some_other_value++;
}
it here will be an iterator which will point to one of the position in map and at max have one first and second value for one iterator . At max you can have multiple key or same key holding same/different values depending on key/value combination.
As far as pushing the value in the vector for a key in map is concern you can do it in the same way you are pushing the key
std::vector<std::string>test;
std::vector<std::string>test2;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
test2.push_back((*it).second);
}
Neways yours question is very unclear .
Just in case you want to deal with different data types in your map I would template a generic copy function:
template <class A, class B>
void mycopy(std::map<A, B>&m, std::list<A>& keys, std::list<B>& values) {
typename std::map<A, B>::iterator it;
for (it = m.begin(); it != m.end(); ++it) {
keys.push_back( (*it).first );
values.push_back( (*it).second );
}
}
Mixing it up:
std::map<int, std::string> mymap;
std::list<int> keys;
std::list<std::string> values;
mymap[1] = "string1";
mymap[2] = "string2";
mycopy(mymap, keys, values);
std::map<std::string, int> mymap1;
std::list<std::string> keys1;
std::list<int> values1;
mymap1["string1"] = 1;
mymap1["string2"] = 2;
mycopy(mymap1, keys1, values1);
Edit: yes __copy isnt the best definition. Thanks