I have several GUIDs and I'd like to implement a hash table to quickly retrieve them. How would I do that?
If I treat the GUIDs as hash codes I need to do something like
index = GUID % prime_number_that_covers_all_GUID_bits
but I'm unsure if this is the right way to do it. How should I do to realize such a hash table?
You could use std::unordered_map, which takes a Key type (GUID) in your case, and a Value type, which could be some user info or program info (depending on your app). Storing is as simple as calling the member functions insert() or emplace() and looking up a stored value is done by calling find().
The example below uses std::string as the underlying type for your keys, and implicitly std::hash<std::string> as the hash function. For other GUID types, you might need to roll your own hash function object and pass that as a template parameter to the hash table.
#include <iostream>
#include <ios>
#include <string>
#include <unordered_map>
typedef std::string GUID;
class UserInfo
{
public:
UserInfo(bool b): is_genius_(b) {}
bool is_genius() const { return is_genius_; }
private:
bool is_genius_;
// your stuff here
};
int main()
{
std::unordered_map<GUID, UserInfo> table;
GUID x = "Johnny Pauling";
// insert into table
table.emplace(x, UserInfo(true));
// lookup in table
auto it = table.find(x);
// if found, print it
if (it != table.end())
std::cout << std::boolalpha << it->second.is_genius();
}
Output on LiveWorkSpace
Related
Say I have the following classes:
#include <string>
class Item {
public:
Item(std::string name, int id);
virtual int getWeight() = 0;
protected:
std::string name;
const int id;
}
#include <vector>
#include <memory>
#include "Item.h"
class Bucket : Item {
public:
Bucket(std::string name, std::string type, int id) // In the implementation this will call the Constructor of Item
bool operator<(const Bucket& b) const {
return (id < b.id );
}
void addItem(std::shared_ptr<Item> itemm) {
this->weight += item->getWeight();
this->items.push_back(item);
}
int getWeight() override; //this implementation does not matter to the problem
private:
std::string type;
std::vector<std::shared_ptr<Item>> items;
int weight = 0;
}
There are other classes inheriting from class Item aswell, but to make it easier I am only showing the class Bucket.
Now in my main method, I want to iterate over a map, that already contains some entries and call a method that changes one property of the key object.
main.cpp:
#include <map>
#include <memory>
#include <vector>
#include "Item.h"
#include "Bucket.h"
using namespace std;
int main(){
map<Bucket, vector<shared_ptr<Item>>> map; // Imagine this map has some entries
for(auto itr = map.begin(); itr != map.end(); itr++){
for_each(itr->second.begin(), itr->second.begin(), [&itr](shared_ptr<Item> item){
itr->first.addItem(item); // This does not compile, the error will be in the text below
});
}
As told in the code, it does not compile with the following error on line itr->first.addItem(item);:
'this' argument to member function 'addItem' has type 'const Bucket', but function is not marked const.
I can't make the method const, since it is changeing some property of the object and I would get an error there.
I am not sure if I understand this error correctly, but I think the problem is, that as I put a Bucket into a map (Bucket is the key), it becomes const.
If that is the case, is there a way to tell the compiler to use only the id property of Bucket as the const key of the map (Without changeing the map to something like map<int, <pair<Bucket,vector<shared_ptr<Item>>>>>)? Or am I not understanding the main problem here?
I think the problem is, that as I put a Bucket into a map (Bucket is the key), it becomes const
Correct. Maps are supposed to be ordered, and if you were able to mutate a key, you could break this invariant. Doing this would potentially break every subsequent search or insert horribly.
The linked workaround is effective, but ugly. Unless map-key-ness is a core feature of your Bucket (I can't tell from the sample code whether that's likely), making some of its members mutable feels like a hack.
Your whole design looks odd, tbh - you're going to end up with a map full of Bucket keys that duplicate the information in the second half of the key,value pair. Are you going to move those Buckets somewhere else afterwards, or are they going to live forever shackled to those vestigial vectors of redundant references?
If that map is an intermediate step in building up connections, it shouldn't be where the Bucket objects live. Perhaps you should have one main lookup map<id, Item>, another transient mapping multimap<id, id> describing which Item contains which other Items.
I'm looking to remove one key - value element from an std::map and keep the value which was stored in this map. Just removing it is not enough, I need the key and value which was stored for other things.
My example is something like this:
std::map<const Class1 *, std::unique_ptr<Class 2>> myMap;
I would like to extract the key and value from the std::map. Simply moving it away is not an option, since this will put the std::map in an incorrect state.
I found the std::extract function, which I can use for an std::set, but not for an std::map. I cannot find any example online showing how I can extract the key and value from this map.
I would like to do something like this:
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.someMethod();
auto value = std::move(keyAndValue.someOtherMethod());
I thought I could use key() and value(), as described in some examples. But this does not work, I get an error.
error C2039: 'value': is not a member of
'std::_Node_handle<std::_Tree_node<std::pair<const
_Kty,_Ty>,std::_Default_allocator_traits<_Alloc>::void_pointer>,_Alloc,std::_Node_handle_map_base,_Kty,_Ty>'
You answered the question by yourself.
I just want to add a complete example. Please see:
#include <iostream>
#include <map>
#include <functional>
#include <memory>
struct Class1
{
void someMethod() const { std::cout << "Some Method from class 1\n";}
};
struct Class2
{
Class2(const int i) : test(i) {}
void someOtherMethod() const { std::cout << "Some other Method from class 2. Index: " << test << '\n';}
int test{0};
};
int main()
{
Class1 cl1_1{}, cl1_2{}, cl1_3{};
std::map<const Class1 *, std::unique_ptr<Class2>> myMap;
// Populate map
myMap[&cl1_1] = std::make_unique<Class2>(1);
myMap[&cl1_2] = std::make_unique<Class2>(2);
myMap[&cl1_3] = std::make_unique<Class2>(3);
// Extract data
auto keyAndValue = myMap.extract(myMap.find(&cl1_1));
// Get key and value
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
// Call methods
key->someMethod();
value->someOtherMethod();
return 0;
}
The answer was suggested by Mark.
For an std::map, I need to use mapped() on the node I get, not value.
Thanks a lot!
So, like this
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
What would be the key in case of Objects being inserted into std::set? As for the following example, I am inserting objects of class Classcomp into the std::set. However, I want to find whether an object of Classcomp with a particular 'id = 1' exists in the std::set or not?
#include <iostream>
#include <set>
struct Classcomp {
int val = 0;
int id = 0; ///ID for the object
bool operator() (const Classcomp& lhs, const Classcomp& rhs) const
{return lhs.val<rhs.val;}
};
int main ()
{
Classcomp c1,c2,c3,c4;
c1.val = 92;c1.id = 2;
c2.val = 94;c2.id = 3;
c3.val = 10;c3.id = 1;
std::set<Classcomp,Classcomp> fifth; // class as Compare
fifth.insert(c1);fifth.insert(c2);fifth.insert(c3);
for (auto x: fifth) {std::cout << x.id << " " << x.val << std::endl;}
if (fifth.find()) {std::cout << "Got it";} //What should I pass as arguments to fifth.find()?
return 0;
}
Sets is different from the map by exactly the fact that values of objects inside the set are their keys. If you need to separate a key from the value, you need a key-value container, such as std::map or std::unordered_map.
Clarification
I am not talking about the option of simply iterating over all objects in the set and checking every object to have specified key - which is easily done with std::find. If that's what you want, please clarify.
As your class is sorted by field val you have following options:
iterate over all elements and find one with particular id by std::find or std::find_if
create another container and sort there by id (maybe using std::share_ptr to avoid duplicates)
use boost::multiindex and create index by val as well as id
I've to store in a data structures tuples of the type
<(1,1),10>
<(1,1),9>
<(2,1),5>
<(1,1),11>
And I need to have just
<(1,1),9>
<(2,1),5>
Which data structure best fit for this kind of problem in c++?
This is my current solution/workaround, i'll keep looking for a more elegant solution btw.
#include <iostream>
#include <stdlib.h>
#include <vector>
#include <boost/unordered_map.hpp>
#include <boost/foreach.hpp>
using namespace std;
typedef pair<int, int> mapkey;
typedef boost::unordered_map<mapkey,float,boost::hash<mapkey> > hmap;
typedef hmap::iterator hitr;
class mymap
{
public:
hmap map;
void insert(std::pair<mapkey,float> p)
{
hmap::iterator i = map.find(p.first);
if(i==map.end()){
map.insert(p);
}
else{
if(p.second<i->second){
i->second=p.second;
}
}
}
};
int main()
{
mymap map;
map.insert(std::make_pair(mapkey(1,1),10.0f));
map.insert(std::make_pair(mapkey(1,2),22.0f));
map.insert(std::make_pair(mapkey(2,1),22.0f));
map.insert(std::make_pair(mapkey(1,1),5.0f));
BOOST_FOREACH(hmap::value_type i, map.map) {
mapkey m = i.first;
std::cout <<"( "<<m.first<<" , "<< m.second<<" ) > " <<i.second<<endl;
}
return 0;
}
just a simple HashMap will do.
Key- class having two attributes, suppose i, j
value - always minimum i.e. a simple int
A hash table, with a custom insert that inserts iff the key is previously unseen or the new value is less than the previous one.
You can use HashMap build-in class in C++.
For you problem, just a simple customisation that you only need to enter pair in HashMap if
1) Key doesn't exist or
2) If key exists, then compare old-value to new-value and then if new-value is less than old value then insert pair in HashMap.
For your reference you can use Simple HashMap implementation in C++
Q#1) Struct below doesn't want to be copied and gives compilation errors - why and how to deal with it?
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct person
{
person(string n)
:name(n)
{}
string name;
};
int main()
{
map<string, person> my_map;
my_map["one"] = person("Tom");
return 0;
}
Q#2) We can avoid the problem above by omitting the struct constructor "person(const string& n)" and assigning struct values one by one:
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct person
{
string name;
};
int main()
{
map<string, person> my_map;
person p;
p.name = "Tom";
my_map["one"] = p;
return 0;
}
So, let's say I do it this way, and after storing many persons in the map I want to check if a particular person exists inside a map. As I know the correct way of doing it is:
if(my_map.find("one") == my_map.end()) { //it doesn't exist in my_map }
else {//it exists}
But this as I understand will iterate through the whole map one by one, won't it? If yes, then is it okay to do it like:
using namespace std;
struct person
{
string name;
string identifier; // <--
};
int main()
{
map<string, person> my_map;
person p;
p.name = "Tom";
p.identifier = "something"; // <--
my_map["one"] = p;
if(my_map["unknown"].identifier == "something") // <--
cout << "Found" << endl;
else
cout << "Not found" << endl;
return 0;
}
By doing this we avoid iterating, and possibility that garbage in the memory will match our identifier is... small I guess, especially if we use some hash.
So is it okay (secure) doing like that?
1) The code in your first example fails to compile because of the following expression:
my_map["one"]
my_map["one"] constructs a std::string from "one", and passes it to std::map::operator[]. map::operator[] ensures that a value is mapped to the supplied key (by associating the key with a default-constructed value if it is not already associated with a value) and returns a reference to that value.
This does not compile, because person does not have a default constructor (A "default constructor" is a constructor which takes no arguments).
There are several ways to fix this problem.
One way is the way that you took - removing the constructor. It works because if you do not supply any constructors, a default constructor will be implicitly defined.
Another way is to explicitly define a default constructor for person:
struct person
{
person():name(){} //or person()=default; if your compiler supports this
person(string n)
:name(n)
{}
string name;
};
Another way is to not use operator[] at all, and to instead use map::insert, as follows:
auto pair(my_map.insert(std::make_pair(std::string("one"),person("Tom"))));
if (!pair.second) {
*pair.first = person("Tom");
}
2) The correct way to find an element in the map is (as you said) to use:
if(my_map.find("one") == my_map.end()) {/*it doesn't exist in my_map*/}
else {/*it exists*/}
This does not inspect every element in the map - in fact it may only inspect O(log(map.size())) elements.
Your fears are totally unfounded, this is the correct way to find an element in the map, however the way in which you continue suggests a severe misunderstanding about what operator[] does.
You ask "what is the probability that my_map["unknown"].identifier == "something" will return true if "unknown" does not exist in the map?".
The answer to this is that there is no chance whatsoever of this returning true, because if no value with the key std::string("unknown") exists in the map, then operator[] will associate std::string("unknown") with a default constructed person, and so identifier will be an empty string.
First of all, since you have a constructor, you need to provide a default constructor. This is because C++ standard library containers use value semantics. So the map needs to be able to copy values, assign them, and default construct them. Since you provide a constructor, the compiler does not synthesize the default constructor. This is a default constructor that does nothing:
person() {} // default constructs string, so no special aciton required.
Particularly in the case of std::map, operator[] returns a reference to a default constructed value when an element with the key does not already exist in the map:
my_map["one"] = p; // creates *default* person, then *assigns* it the value of p.
Second, concerning your question about searching the map, std::map, search has logarithmic complexity and is typically implemented as a self-balancing binary tree. So when you search you do not traverse the whole map. And since accessing via operator[] introduces new elements when the searched key doesn't exist, the form using find() is the canonical way to do it.
Since you mentioned hashing, C++11 provides std::unordered_map, and tr1 and boost have hash_map. These use hash functions perform the search is constant time. Whether it is worth using it or not depends on factors such as the size of your map. The constant time could be larger than the logarithmic time taken to search a small map.
Note:
If you want to use your struct as key, or want to insert it into one of the standard library sets, you have further requirements:
maps: You need to provide strict weak ordering, for the key, either via a less-than operator for your class, or a custom comparator functor. If you were to use person as a key, you woul dneed something like:
bool operator<(const person& rhs) const { return name < rhs.name; }
unordered_ or hash maps: You must provide both a hash function and an equality comparison for the key, either via an operator== or a functor. .