How can I change the value in a pair in maps - c++

I can do:
map<char*, int> counter;
++counter["apple"];
But when I do:
--counter["apple"] // when counter["apple"] ==2;
I got debugger hung up in VS 2008.
Any hints?

Do you rely on the value of it? A string literal is not required to have the same address in different uses of it (especially when used in different translation units). So you may actually create two values by this:
counter["apple"] = 1;
counter["apple"] = 1;
Also you get no kind of any sorting, since what happens is that it sorts by address. Use std::string which does not have that problem as it's aware of the content and whose operator< compares lexicographical:
map<std::string, int> counter;
counter["apple"] = 1;
assert(++counter["apple"] == 2);

A map of the form:
map <char *, int> counter;
is not a very sensible structure, because it cannot manage the char pointers it contains effectively. Change the map to:
map <string, int> counter;
and see if that cures the problem.

I found the problem.
If I change it to:
map<string,int> counter;
counter["apple"]++;
if(counter["apple"]==1)
counter.erase("apple");
else
counter["apple"]--; //this will work
In the Key/value pair, if value is a int and value ==1, I somehow could not do map[key]--, ('cause that will make the value ==0?)

Related

How to return a value from std::map<int, std::string> with a uint64_t key type?

Novice question, but I searched for this and couldn't find something clearly solving my issue - apologies if this is obvious.
I have defined a map which looks like this:
map<int, string> testmap = {
{ 0, "a" },
{ 1, "b" },
{ 2, "c" }
}
However, I need to retrieve a value from testmap using a uint64_t value provided by another function.
When I do testmap[my_uint64_t_value] it returns an empty string, so I think this is because it's adding my_uint64_t_value as a key and setting the value to NULL.
This is the same if I set the map type to <uint64_t, string>, at least the way I'm currently defining my keys.
However, is there a way that I either:
convert the uint64_t value to a regular int
define the map as <uint64_t, string>, and be able to define my
keys as the 'correct' type?
It seems like int type conversion isn't that common, is this something that should be avoided?
The reason why you get an empty string is std::map::operator[] returns a reference to the value if and only if it exists, otherwise it performs an insertion. I suspect you have the latter case.
You need to use std::map::find for search.
uint64_t keyToFind = 1;
if (auto iter = testmap.find(keyToFind); iter != testmap.cend())
{
// do something
std::cout << "Found!\n";
}
else { std::cout << "Not Found!\n"; }
Like #Rene mentioned in the comments, casting from uint64_t to int can cause overflow. Therefore, making the key to larger type(as per requirement) would be a good idea.
std::map<uint64_t, std::string> testmap;
As said in another answer, the [] operator of the map class will perform an insertion with a default-constructed value if the key is not present in the map.
You can first use the count method to determine if the key is present in the map before accessing it.
if(testmap.count(keyToFind))
return testmap[keyToFind];
else
report_key_not_found();
An alternative solution is to use the at method to access the value. It will throw an std::out_of_range exception if the key is not present instead of inserting a new key.

Using a vector < pair < string, int > > to represent a hash table

I am trying to represent a hash table as a vector of pair < string, int>. I am using a hash function to return the value of the index of the vector where I wish to place the pair. I have been able to successfully create a pair and index the pair's string with the hash function. Now that I know where I want to place my pair in my vector I try to put it there but my program has a segmentation fault at this point.
My hash function:
size_t hashfunction(const string& ident){
unsigned hash = 0;
for(int i = 0; i < ident.size(); ++i) {
char c = ident[i];
hash ^= c + 0x9e3779b9 + (hash<<6) + (hash>>2);
}
return hash;
}
My main function:
int main(){
vector < pair < string, int > > hashtable;
pair <string, int> testone ("bartering", 5);
size_t testoneindex = hashfunction(testone.first);
hashtable[testoneindex] = testone;
return 0;
}
This section of code compiles but produces a segmentation fault at the line
hashtable[testoneindex] = testone;
What am I doing wrong?
You cannot realistically have your container done this way because of the memory required. Instead you'd want the container and insertion code to be closer to classic hash container design, something like this:
typedef pair <string, int> value_t;
value_t val;
vector<list<value_t>> buckets;
buckets.resize(current_size);
auto& bucket = buckets[hashfunc(val.first) % buckets.size()];
auto itr = find_if(bucket.begin(), bucket.end(), [&](value_t const& other) {
return other.first == val.first;
});
if (itr == bucket.end()) bucket.push_back(val);
You need to modulo your hash index down to the range of indices in your vector. For example, initialize your vector to have 1000 buckets, and use hashfunction(..) % 1000.
The std::vector<...> you created is empty. Placing an object anywhere in this object won't work. You need to resize the hashtable object to a suitable size, i.e., you need to give that object the number of buckets, e.g., using
std::size_t number_of_buckets = ...;
std::vector<std::pair<std::string, int> > hashtable(number_of_buckets);
Note, that the approach you take for hashing is a bit too simplistic, though: especially for smaller number of buckets there is a chance that two different hashes as keyed to the same bucket. That is, you'll need to deal with collisions. The two approaches for dealing with collisions I'm aware of are
Determine a new bucket with a key if the first bucket found is already used (and keep searching for new buckets until an empty bucket is found). The main issue with this approach is that you can't really remove objects as other rebucketed objects can be found.
Use a list in each bucket for all the keys which use the same bucket. This approach has the additional advantage that you don't need to create any key or value until a bucket is actually used (you'd have the lists, though, but this can be made fairly cheap).

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.

std::map keys in C++

I have a requirement to create two different maps in C++. The Key is of type CHAR* and the Value is a pointer to a struct. I am filling 2 maps with these pairs, in separate iterations. After creating both maps I need find all such instances in which the value of the string referenced by the CHAR* are same.
For this I am using the following code :
typedef struct _STRUCTTYPE
{
..
} STRUCTTYPE, *PSTRUCTTYPE;
typedef pair <CHAR *,PSTRUCTTYPE> kvpair;
..
CHAR *xyz;
PSTRUCTTYPE abc;
// after filling the information;
Map.insert (kvpair(xyz,abc));
// the above is repeated x times for the first map, and y times for the second map.
// after both are filled out;
std::map<CHAR *, PSTRUCTTYPE>::iterator Iter,findIter;
for (Iter=iteratedMap->begin();Iter!=iteratedMap->end();mapIterator++)
{
char *key = Iter->first;
printf("%s\n",key);
findIter=otherMap->find(key);
//printf("%u",findIter->second);
if (findIter!=otherMap->end())
{
printf("Match!\n");
}
}
The above code does not show any match, although the list of keys in both maps show obvious matches. My understanding is that the equals operator for CHAR * just equates the memory address of the pointers.
My question is, what should i do to alter the equals operator for this type of key or could I use a different datatype for the string?
My understanding is that the equals operator for CHAR* just equates the memory address of the pointers.
Your understanding is correct.
The easiest thing to do would be to use std::string as the key. That way you get comparisons for the actual string value working without much effort:
std::map<std::string, PSTRUCTTYPE> m;
PSTRUCTTYPE s = bar();
m.insert(std::make_pair("foo", s));
if(m.find("foo") != m.end()) {
// works now
}
Note that you might leak memory for your structs if you don't always delete them manually. If you can't store by value, consider using smart pointers instead.
Depending on your usecase, you don't have to neccessarily store pointers to the structs:
std::map<std::string, STRUCTTYPE> m;
m.insert(std::make_pair("foo", STRUCTTYPE(whatever)));
A final note: typedefing structs the way you are doing it is a C-ism, in C++ the following is sufficient:
typedef struct STRUCTTYPE {
// ...
} *PSTRUCTTYPE;
If you use std::string instead of char * there are more convenient comparison functions you can use. Also, instead of writing your own key matching code, you can use the STL set_intersection algorithm (see here for more details) to find the shared elements in two sorted containers (std::map is of course sorted). Here is an example
typedef map<std::string, STRUCTTYPE *> ExampleMap;
ExampleMap inputMap1, inputMap2, matchedMap;
// Insert elements to input maps
inputMap1.insert(...);
// Put common elements of inputMap1 and inputMap2 into matchedMap
std::set_intersection(inputMap1.begin(), inputMap1.end(), inputMap2.begin(), inputMap2.end(), matchedMap.begin());
for(ExampleMap::iterator iter = matchedMap.begin(); iter != matchedMap.end(); ++iter)
{
// Do things with matched elements
std::cout << iter->first << endl;
}