Not updating struct values in map of multimaps - c++

I'm using C++ and have a number of data structures that I need to update things in. I use a map from double to multi maps of int and my struct. It looks like this:
std::map<double, std::multimap<int, dnode*> *> m_ptable;
My struct definition looks like this:
struct dnode{
std::string ticker;
double buy;
double sell;
//(sell/buy)
double result;
bool hasDuplicate;
};
The issue is that I need to iterate through all of the dnodes in the multi map and update their value of "sell" and "result" because this has to be computed after all of the nodes are already in the multi map. Here is the function I wrote to do that:
void dataProcessor::addCloseToEntry(double close){
map<double, std::multimap<int, dnode*> *>::iterator it;
multimap<int, dnode*>::iterator mm_it;
for(it = m_ptable.begin(); it != m_ptable.end(); it++){
for(mm_it = (it->second)->begin(); mm_it != (it->second)->end(); mm_it++){
mm_it->second->sell = close;
mm_it->second->result = close/(mm_it->second->buy);
}
}
return;
}
When I step through this code I see that the values for "sell" and "result" are not changing, but remain as 0 (I initialize them to zero when I add a new node into the map.)
Is there something I'm doing incorrectly with the iterators here that's causing this?
Thanks!

Ok this code works fine. My logic was messed up in the lines before this. This function was called inside a while loop with a parameter that was reinitialized to zero before it was called every time.
Wow.
Thanks anyway guys!

Related

Loss of data while ordering an unordered_map c++

I have a unordered_map<string, int> freq and I order it transforming it into a
map<int,string> freq2. I use the next function in order to do that:
map<int, string> order(unordered_map<string, int> x) {
map <int, string> map;
for (auto it = x.begin(); it != x.end(); ++it) {
map.emplace(it->second, it->first);
}
return map;
}
the size of the unordered_mapis 2355831 and the returned map is 505, so as you see the loss of data is quite big and i have no idea why....
Any idea why this happens?
Thanks.
EDIT:
Thanks to all, you are all right, I have a lot of int with same value, that´s why i loose the data( really stupid from my part to not see it before)
Most likely this is because there are duplicates among the int values. Try replacing map<int, string> with multimap<int, string>.
The code itself looks fine. However, since you are mapping from string keys to integers, it might be very well that you have multiple keys with the same value.
From the documentation of emplace:
The insertion only takes place if no other element in the container has a key equivalent to the one being emplaced (keys in a map container are unique).
So if a lot of your entries in the first map have the same value (which is the key in the second map), then your dataset will decrease by a lot.
If you need to preserve those elements, then std::map is not the right container.

combining map and array of string c++

I'm writing a code to check if a person have visited a particular place before or not
if true do nothing else add the new place to the list of visited place
I'm using map to store the student name as a key and array the store places as follow
#include<map>
using namespace std;
map<string,string[]> stu_plac;
map<string,string[])::iterator it;
I could not find the roght way to search across the map
I tried the following:
bool exist=false;
if(stu_plac.count(name))
{
it=stu_plac.find(name);
for(auto tt=(*it).second.begin(); tt!=(*it).second.end(); tt++)
{
if (tt.compare(place)==0)//exist
{
exist=true;
}
break;
}
if (!exist)
{
(*it)second[++tt]=place;
}
}
else
{
stu_plac.insert(pair<string,string[]>(name,place));
}
I think the problem is with the array of string iterator, can you please help me to find the correct way to do this. Do I need to use multimap or other data structer to do this??
Data structure like map<string, vector<string> > will work.
You can use for (size_t i = 0; i < it->second.size(); i++) to traverse the vector.
It looks like you wanted a map of string -> places
typedef map<string,std::set<string> > Map;
Adapting it for your snippet (which had many problems, starting with the confusing formatting...):
#include<map>
#include<set>
#include<string>
using namespace std;
typedef map<string,std::set<string> > Map;
typedef Map::iterator It;
int main()
{
bool exist=false;
Map stu_plac;
string name = "name";
string place = "0.0.0.0";
It it = stu_plac.find(name);
if (stu_plac.end() != it)
{
std::set<string>& visits = it->second;
exist = visits.end() != visits.find(place);
if (!exist)
visits.insert(place);
}
}
But in reality you may prefer to use std::multimap
I wouln't use an array because I can't know it's size, I prefer using a vector or a list, and instead of using a boolean variable I would insert and then break the loop, so my algorithm would be like:
std::map<std::string, std::list<std::string> > stu_plac;
std::map<std::string, std::list<std::string> >::iterator it;
std::string myName("Edu");
std::string myPlace("Madrid");
.....
for( it = stu_plac.begin(); it != stu_plac.end(); it++){
if(it->first == myName){
std::list<std::string>::iterator place = std::find(it->second.begin(), it->second.end(), myPlace);
if(place == it->second.end()){
it->second.push_back(myPlace);
break;
}
}
}
Look that you can use the iterator you got to add the city you want.
By the way, I wouldn't use "auto" variable if it's not C++11.
An array in C++ is one of the types or constructs inherited from C so the built-in array "type" doesn't have any iterators. It also doesn't track its current size so you cannot get begin and end iterators of an array without also storing its size.
Use std::map< std::string, std::vector<std::string> > instead.

Sigbus on std::map.find

I have got a map defined inside a class in header file like this:
std::map<int, char> mCompletedIds;
I can do with a vector here, as I just need to store the ids which have been completed. But to have fast find, I am using map. So, I always put second argument of pair as 0.
Now, In one of the fns. of class, I am doing find.
std::map<int, char>::iterator it = mCompletedIds.find(id); //id is defined above
On this statement, I am getting SIGBUS. Is it because map is empty at the moment? Can anyone pls. help me understand the reason.
Thanks,
sg
If you just want a store of numbers you can use std::set<int>
To see if a value is present use
std::set<int> mCompletedIds;
bool found = mCompletedIds.count(id) != 0;
Your SIGBUS error will usually be caused by bad alignment or some other corruption that happened in your code, and a tool like valgrind may indicate to you where your real error is.
std::set is what you need : cppRef
int id = 0;
std::set<int> idList;
std::set<int>::iterator it = idList.find(id); //see also count function
if (it != idList.end()) { // find will return set::end if the element is not found
// id is in your list
} else {
// id isn't in your list
}

Storing and accessing a collection of strings (STD C++)

SKU1 SKU2 Description
"01234" "34545" "White Bread"
"01545" "34236" "Wheat Bread"
I need to cross-reference these three fields, i.e. retrieve SKU2 while knowing SKU1, SKU1 while knowing SKU2, and Description while knowing either SKU1 or SKU2.
I'm curious - what is the best way to do this? Vectors using search() or find()? Using a map somehow?
I currently have it working using a vector< vector<string> >, looping through the 'parent' vectors and the 'child' vectors, comparing the values, but this seems primitive.
Basically, I need a vector that uses any of its strings as an index to return one of the two other values. Is the general way I'm doing it considered acceptable/optimal?
vector< vector<string> > products;
int i = 0;
for( i = 0; i < 2; ++i)
{
products.push_back( vector<string>() );
products[i].push_back( "SKU1" );
products[i].push_back( "SKU2" );
products[i].push_back( "Description" );
}
Thanks for your assistance.
Boost BiMap.
I would recommend using two maps that index into an object that has the information you need:
struct MyInfo
{
std::string SKU1;
std::string SKU2;
std::string Description;
};
std::map<std::string, MyInfo *> SKU1map;
std::map<std::string, MyInfo *> SKU2map;
MyInfo * newProduct = new MyInfo; ///Do not forget to delete!!
newProduct->SKU1 = //SKU1 value
newProduct->SKU2 = //SKU2 value
newProduct->Description = //Description value
SKU1map[newProduct->SKU1] = newProduct;
SKU2map[newProduct->SKU2] = newProduct;
This will be a decently fast implementation(much better than linear search), and if you deal with many product instances, then it will also be more memory efficient.
Build three std::map<std::string, std::string>s: one to map SKU1s to SKU2s, one to map SKU1s to Descriptions, and one to map SKU2s to Descriptions. (Better yet, use std::unordered_map, if you have it (C++0x)).
This is assuming that you have a lot of data and are prioritizing speed rather than memory usage.

C++ STL map I don't want it to sort!

This is my code
map<string,int> persons;
persons["B"] = 123;
persons["A"] = 321;
for(map<string,int>::iterator i = persons.begin();
i!=persons.end();
++i)
{
cout<< (*i).first << ":"<<(*i).second<<endl;
}
Expected output:
B:123
A:321
But output it gives is:
A:321
B:123
I want it to maintain the order in which keys and values were inserted in the map<string,int>.
Is it possible? Or should I use some other STL data structure? Which one?
There is no standard container that does directly what you want. The obvious container to use if you want to maintain insertion order is a vector. If you also need look up by string, use a vector AND a map. The map would in general be of string to vector index, but as your data is already integers you might just want to duplicate it, depending on your use case.
Like Matthieu has said in another answer, the Boost.MultiIndex library seems the right choice for what you want. However, this library can be a little tough to use at the beginning especially if you don't have a lot of experience with C++. Here is how you would use the library to solve the exact problem in the code of your question:
struct person {
std::string name;
int id;
person(std::string const & name, int id)
: name(name), id(id) {
}
};
int main() {
using namespace::boost::multi_index;
using namespace std;
// define a multi_index_container with a list-like index and an ordered index
typedef multi_index_container<
person, // The type of the elements stored
indexed_by< // The indices that our container will support
sequenced<>, // list-like index
ordered_unique<member<person, string,
&person::name> > // map-like index (sorted by name)
>
> person_container;
// Create our container and add some people
person_container persons;
persons.push_back(person("B", 123));
persons.push_back(person("C", 224));
persons.push_back(person("A", 321));
// Typedefs for the sequence index and the ordered index
enum { Seq, Ord };
typedef person_container::nth_index<Seq>::type persons_seq_index;
typedef person_container::nth_index<Ord>::type persons_ord_index;
// Let's test the sequence index
persons_seq_index & seq_index = persons.get<Seq>();
for(persons_seq_index::iterator it = seq_index.begin(),
e = seq_index.end(); it != e; ++it)
cout << it->name << ":"<< it->id << endl;
cout << "\n";
// And now the ordered index
persons_ord_index & ord_index = persons.get<Ord>();
for(persons_ord_index::iterator it = ord_index.begin(),
e = ord_index.end(); it != e; ++it)
cout << it->name << ":"<< it->id << endl;
cout << "\n";
// Thanks to the ordered index we have fast lookup by name:
std::cout << "The id of B is: " << ord_index.find("B")->id << "\n";
}
Which produces the following output:
B:123
C:224
A:321
A:321
B:123
C:224
The id of B is: 123
Map is definitely not right for you:
"Internally, the elements in the map are sorted from lower to higher key value following a specific strict weak ordering criterion set on construction."
Quote taken from here.
Unfortunately there is no unordered associative container in the STL, so either you use a nonassociative one like vector, or write your own :-(
I had the same problem every once in a while and here is my solution: https://github.com/nlohmann/fifo_map. It's a header-only C++11 solution and can be used as drop-in replacement for a std::map.
For your example, it can be used as follows:
#include "fifo_map.hpp"
#include <string>
#include <iostream>
using nlohmann::fifo_map;
int main()
{
fifo_map<std::string,int> persons;
persons["B"] = 123;
persons["A"] = 321;
for(fifo_map<std::string,int>::iterator i = persons.begin();
i!=persons.end();
++i)
{
std::cout<< (*i).first << ":"<<(*i).second << std::endl;
}
}
The output is then
B:123
A:321
Besides Neil's recommendation of a combined vector+map if you need both to keep the insertion order and the ability to search by key, you can also consider using boost multi index libraries, that provide for containers addressable in more than one way.
maps and sets are meant to impose a strict weak ordering upon the data. Strick weak ordering maintains that no entries are equavalent (different to being equal).
You need to provide a functor that the map/set may use to perform a<b. With this functor the map/set sorts its items (in the STL from GCC it uses a red-black tree). It determines weather two items are equavalent if !a<b && !b<a -- the equavelence test.
The functor looks like follows:
template <class T>
struct less : binary_function<T,T,bool> {
bool operator() (const T& a, const T& b) const {
return a < b;
}
};
If you can provide a function that tells the STL how to order things then the map and set can do what you want. For example
template<typename T>
struct ItemHolder
{
int insertCount;
T item;
};
You can then easily write a functor to order by insertCount. If your implementation uses red-black trees your underlying data will remain balanced -- however you will get a lot of re-balancing since your data will be generated based on incremental ordering (vs. Random) -- and in this case a list with push_back would be better. However you cannot access data by key as fast as you would with a map/set.
If you want to sort by string -- provide the functor to search by string, using the insertCount you could potentiall work backwards. If you want to search by both you can have two maps.
map<insertcount, string> x; // auxhilary key
map<string, item> y; //primary key
I use this strategy often -- however I have never placed it in code that is run often. I'm considering boost::bimap.
Well, there is no STL container which actually does what you wish, but there are possibilities.
1. STL
By default, use a vector. Here it would mean:
struct Entry { std::string name; int it; };
typedef std::vector<Entry> container_type;
If you wish to search by string, you always have the find algorithm at your disposal.
class ByName: std::unary_function<Entry,bool>
{
public:
ByName(const std::string& name): m_name(name) {}
bool operator()(const Entry& entry) const { return entry.name == m_name; }
private:
std::string m_name;
};
// Use like this:
container_type myContainer;
container_type::iterator it =
std::find(myContainer.begin(), myContainer.end(), ByName("A"));
2. Boost.MultiIndex
This seems way overkill, but you can always check it out here.
It allows you to create ONE storage container, accessible via various indexes of various styles, all maintained for you (almost) magically.
Rather than using one container (std::map) to reference a storage container (std::vector) with all the synchro issues it causes... you're better off using Boost.
For preserving all the time complexity constrains you need map + list:
struct Entry
{
string key;
int val;
};
typedef list<Entry> MyList;
typedef MyList::iterator Iter;
typedef map<string, Iter> MyMap;
MyList l;
MyMap m;
int find(string key)
{
Iter it = m[key]; // O(log n)
Entry e = *it;
return e.val;
}
void put(string key, int val)
{
Entry e;
e.key = key;
e.val = val;
Iter it = l.insert(l.end(), e); // O(1)
m[key] = it; // O(log n)
}
void erase(string key)
{
Iter it = m[key]; // O(log n)
l.erase(it); // O(1)
m.erase(key); // O(log n)
}
void printAll()
{
for (Iter it = l.begin(); it != l.end(); it++)
{
cout<< it->key << ":"<< it->val << endl;
}
}
Enjoy
You could use a vector of pairs, it is almost the same as unsorted map container
std::vector<std::pair<T, U> > unsorted_map;
Use a vector. It gives you complete control over ordering.
I also think Map is not the way to go. The keys in a Map form a Set; a single key can occur only once. During an insert in the map the map must search for the key, to ensure it does not exist or to update the value of that key. For this it is important (performance wise) that the keys, and thus the entries, have some kind of ordering. As such a Map with insert ordering would be highly inefficient on inserts and retrieving entries.
Another problem would be if you use the same key twice; should the first or the last entry be preserved, and should it update the insert order or not?
Therefore I suggest you go with Neils suggestion, a vector for insert-time ordering and a Map for key-based searching.
Yes, the map container is not for you.
As you asked, you need the following code instead:
struct myClass {
std::string stringValue;
int intValue;
myClass( const std::string& sVal, const int& iVal ):
stringValue( sVal ),
intValue( iVal) {}
};
std::vector<myClass> persons;
persons.push_back( myClass( "B", 123 ));
persons.push_back( myClass( "A", 321 ));
for(std::vector<myClass>::iterator i = persons.begin();
i!=persons.end();
++i)
{
std::cout << (*i).stringValue << ":" << (*i).intValue << std::endl;
}
Here the output is unsorted as expected.
Map is ordered collection (second parametr in template is a order functor), as set. If you want to pop elements in that sequenses as pushd you should use deque or list or vector.
In order to do what they do and be efficient at it, maps use hash tables and sorting. Therefore, you would use a map if you're willing to give up memory of insertion order to gain the convenience and performance of looking up by key.
If you need the insertion order stored, one way would be to create a new type that pairs the value you're storing with the order you're storing it (you would need to write code to keep track of the order). You would then use a map of string to this new type for storage. When you perform a look up using a key, you can also retrieve the insertion order and then sort your values based on insertion order.
One more thing: If you're using a map, be aware of the fact that testing if persons["C"] exists (after you've only inserted A and B) will actually insert a key value pair into your map.
Instead of map you can use the pair function with a vector!
ex:
vector<::pair<unsigned,string>> myvec;
myvec.push_back(::pair<unsigned,string>(1,"a"));
myvec.push_back(::pair<unsigned,string>(5,"b"));
myvec.push_back(::pair<unsigned,string>(3,"aa"));`
Output:
myvec[0]=(1,"a"); myvec[1]=(5,"b"); myvec[2]=(3,"aa");
or another ex:
vector<::pair<string,unsigned>> myvec2;
myvec2.push_back(::pair<string,unsigned>("aa",1));
myvec2.push_back(::pair<string,unsigned>("a",3));
myvec2.push_back(::pair<string,unsigned>("ab",2));
Output: myvec2[0]=("aa",1); myvec2[1]=("a",3); myvec2[2]=("ab",2);
Hope this can help someone else in the future who was looking for non sorted maps like me!
struct Compare : public binary_function<int,int,bool> {
bool operator() (int a, int b) {return true;}
};
Use this to get all the elements of a map in the reverse order in which you entered (i.e.: the first entered element will be the last and the last entered element will be the first). Not as good as the same order but it might serve your purpose with a little inconvenience.
Use a Map along with a vector of iterators as you insert in Map. (Map iterators are guaranteed not to be invalidated)
In the code below I am using Set
set<string> myset;
vector<set<string>::iterator> vec;
void printNonDuplicates(){
vector<set<string>::iterator>::iterator vecIter;
for(vecIter = vec.begin();vecIter!=vec.end();vecIter++){
cout<<(*vecIter)->c_str()<<endl;
}
}
void insertSet(string str){
pair<set<string>::iterator,bool> ret;
ret = myset.insert(str);
if(ret.second)
vec.push_back(ret.first);
}
If you don't want to use boost::multi_index, I have put a proof of concept class template up for review here:
https://codereview.stackexchange.com/questions/233157/wrapper-class-template-for-stdmap-stdlist-to-provide-a-sequencedmap-which
using std::map<KT,VT> and std::list<OT*> which uses pointers to maintain the order.
It will take O(n) linear time for the delete because it needs to search the whole list for the right pointer. To avoid that would need another cross reference in the map.
I'd vote for typedef std::vector< std::pair< std::string, int > > UnsortedMap;
Assignment looks a bit different, but your loop remains exactly as it is now.
There is std::unordered_map that you can check out. From first view, it looks like it might solve your problem.