How can I store objects of a class in an unordered_set
? My program needs to frequently check if an object exists in this unordered_set
and if it does,
You could try using the following hash function object (it's pretty basic so you may want to improve it to avoid too many collisions).
struct node_hash {
std::size_t operator()(const node& _node) const {
return std::hash<std::string>()(_node.node_id);
}
}
// ...
std::unordered_set<node, node_hash> node_set;
However, as one of the comments points out, you may be better off using a std::unordered_map<std::string, double>
here.
You need to implement a custom hash function (I'd suggest using the function in the Boost library) to do this. C++ allows you to save pointers to objects of a class using unordered_set. For most purposes, that should do the trick.
Since this is the top Google result on Stack Overflow for C++ unordered_set of objects
I'll post a simple yet completely illustrative and copy/paste runnable example:
// UnorderedSetOfObjects.cpp
#include <iostream>
#include <vector>
#include <unordered_set>
struct Point
{
int x;
int y;
Point() { }
Point(int x, int y)
{
this->x = x;
this->y = y;
}
bool operator==(const Point& otherPoint) const
{
if (this->x == otherPoint.x && this->y == otherPoint.y) return true;
else return false;
}
struct HashFunction
{
size_t operator()(const Point& point) const
{
size_t xHash = std::hash<int>()(point.x);
size_t yHash = std::hash<int>()(point.y) << 1;
return xHash ^ yHash;
}
};
};
int main(void)
{
std::unordered_set<Point, Point::HashFunction> points;
points.insert(Point(1, 1));
points.insert(Point(2, 2));
points.insert(Point(1, 1)); // notice this is a duplicate with the 1st point so it won't change the set
std::cout << "points: " << "\n";
for (auto& point : points)
{
std::cout << "(" << point.x << ", " << point.y << ")" << "\n";
}
return 0;
}
I agree to sjrowlinson that for your specific use case an std::unordered_map<std::string, double>
might be the better choice. However, if you want to stick to an unordered_set
due to some reason, then you can also use a lambda expression instead of defining a hash function. But you also have to provide a comparison function (equal
) to make your code working. If you want two node
instances to be equal if they have the same node_id
, then you can use the following code:
auto hash = [](const node& n){ return std::hash<std::string>()(n.node_id); };
auto equal = [](const node& n1, const node& n2){ return n1.node_id == n2.node_id; };
std::unordered_set<node, decltype(hash), decltype(equal)> set(8, hash, equal);
However, if you want to use std::unordered_set::find(), then you cannot simply provide a string (e.g. "1001"
) to that function, because it expects a node
object as parameter. The following code (which creates a temporary object) does the trick, though:
set.insert(node("1001", 100));
if (set.find(node("1001", 0)) != set.end())
std::cout << "1001 found" << std::endl;
Please note that the output 1001 found
is printed, although the value
of the inserted node
is different from the value
of the node
given to the find()
function (100 and 0, respectively). This is, because the comparison function equal
only considers the node_id
when checking for equality.
Code on Ideone