Vectors, structs and std::find - c++

Again me with vectors. I hope I'm not too annoying. I have a struct like this :
struct monster
{
DWORD id;
int x;
int y;
int distance;
int HP;
};
So I created a vector :
std::vector<monster> monsters;
But now I don't know how to search through the vector. I want to find an ID of the monster inside the vector.
DWORD monster = 0xFFFAAA;
it = std::find(bot.monsters.begin(), bot.monsters.end(), currentMonster);
But obviously it doesn't work. I want to iterate only through the .id element of the struct, and I don't know how to do that. Help is greatly appreciated. Thanks !

std::find_if:
it = std::find_if(bot.monsters.begin(), bot.monsters.end(),
boost::bind(&monster::id, _1) == currentMonster);
Or write your own function object if you don't have boost. Would look like this
struct find_id : std::unary_function<monster, bool> {
DWORD id;
find_id(DWORD id):id(id) { }
bool operator()(monster const& m) const {
return m.id == id;
}
};
it = std::find_if(bot.monsters.begin(), bot.monsters.end(),
find_id(currentMonster));

how about:
std::find_if(monsters.begin(),
monsters.end(),
[&cm = currentMonster]
(const monster& m) -> bool { return cm == m; });

You need to write your own search predicate:
struct find_monster
{
DWORD id;
find_monster(DWORD id) : id(id) {}
bool operator () ( const monster& m ) const
{
return m.id == id;
}
};
it = std::find_if( monsters.begin(), monsters.end(), find_monster(monsterID));

Take a look at the std::find template, the third parameter especially:
template<class InputIterator, class EqualityComparable>
InputIterator find(InputIterator first, InputIterator last,
const EqualityComparable& value);
What is this EqualityComparable? Again from the documentation:
A type is EqualityComparable if objects of that type can be
compared for equality using operator==, and if operator== is
an equivalence relation.
Now, your type monster needs to define such an operator. If you don't the compiler generates one for you (as also the default ctor and the dtor) which does a memcmp sort of thing which doesn't work in your case. So, to use std::find first define a comparator function/functor that the algorithm can use to match your currentMonster i.e. something along the lines of:
struct monster {
// members
bool operator==(const monster& l, const monster& r) const
{
return l.id == r.id;
}
};

or put the monsters in a map instead of a vector
or if they must be in a vector create an index map ie map of ID to vector index

This is a complete sample based on the answer of Johannes Schaub (boost version).
#include <algorithm>
#include <boost/bind.hpp>
struct monster
{
DWORD id;
int x;
int y;
int distance;
int HP;
};
int main ()
{
std::vector<monster> monsters;
monster newMonster;
newMonster.id = 1;
newMonster.x = 10;
monsters.push_back ( newMonster );
newMonster.id = 2;
newMonster.x = 20;
monsters.push_back ( newMonster );
newMonster.id = 2;
newMonster.x = 30;
monsters.push_back ( newMonster );
DWORD monsterId = 2;
std::vector< monster >::iterator it = std::find_if ( monsters.begin (), monsters.end (),
boost::bind ( &monster::id, _1 ) == monsterId );
return 0;
}

You can write a function as below:
monster* findMonster(DWORD currentMonster) {
for (auto it = bot.monsters.begin(); it != bot.monsters.end(); it++) {
if (it->id == currentMonster) {
return &(*it);
}
}
return NULL;
}
It returns a pointer to the stored node if it's found in the vector, otherwise returns NULL.
Please note that return it; won't work directly.

Related

How to search for an integer within a nested class with the binary_search STL function?

How to search for an integer within a nested class with the binary_search STL function? Is it possible to do a binary search on the vector vITems to search for the product class ID? Currently, I have to populate a std::vector<Product> vProd array inside the Order class to use the binary_search function.
I did a lot of research on the subject, but I was unsuccessful in solving the problem.
#include <iostream>
#include <algorithm>
#include <vector>
class Product {
public:
int Id;
std::string Name;
Product(int idProd, const std::string &nameProd) : Id(idProd), Name(nameProd) {}
Product(int idProd) : Id(idProd) {} // Required for lambda function to work on binary_search function
};
class Item {
public:
Product Prod;
int Number;
Item(Product &prod, int numProd) : Prod(prod), Number(numProd) {}
};
class Order{
private:
std::vector<Item> vItems;
public:
bool consultProd(int idProd) const {
std::vector<Product> vProd;
size_t total = vItems.size();
for(size_t i = 0; i < total; i++)
vProd.push_back(vItems[i].Prod);
bool yesId = binary_search( vProd.begin(), vProd.end(), idProd,
[]( const Product &p1, const Product &p2)
{
return p1.Id < p2.Id;
} );
return yesId;
}
void setItem(Item &it){
vItems.push_back(it);
}
};
int main()
{
//----------------------------------------------------------------
Product p1(1, "aaa"), p2(2, "bbb"), p3(3, "ccc"), p4(4, "ddd");
Item it1(p1, 1), it2(p2, 3), it3(p3, 3), it4(p4, 7);
Order ord;
ord.setItem(it1);
ord.setItem(it2);
ord.setItem(it3);
ord.setItem(it4);
//----------------------------------------------------------------
if( !ord.consultProd(2) )
ord.setItem(it2);
else
std::cout << "Warning: Product already included in the order.\n";
system("pause");
return 0;
}
With helper:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
You might do with custom comparer:
bool consultProd(int idProd) const {
return binary_search(
vItems.begin(),
vItems.end(),
idProd,
overloaded{[](int id, const Item& item) { return id < item.Prod.Id; },
[](const Item& item, int id) { return item.Prod.Id < id; }
});
}
Demo
You can use std::lower_bound:
const auto item_it = std::lower_bound(vItems.begin(), vItems.end(), idProd,
[](const Item& item, int id) { return item.Prod.Id < id; });
const bool yesId = (item_it != vItems.end() && item_it->Prod.Id == idProd);
return yesId;
Note that [vItems.begin(), vItems.end()) should represent a valid range of items sorted by Prod.Id or at least partitioned with respect to Prod.Id < idProd. If this condition is not fulfilled, the behaviour of std::lower_bound is undefined.
What is the correct way to do this ordering?
If the range is not sorted, the correct way is to use std::find_if:
const auto item_it = std::find_if(vItems.begin(), vItems.end(),
[idProd](const Item& item) { return item.Prod.Id == idProd; });
const bool yesId = (item_it != vItems.end());
Sorting before each binary search doesn't make sense: binary search takes O(log n) time, but sorting takes O(n log n) time. This is worse than linear time complexity O(n) provided by std::find_if.

Comparing function of binary_search

I am trying to run binary_search on vector of custom objects.
struct T{
string name;
T(string n):name(n){};
bool operator < ( T * n ) const {
return name < n -> name;
}
bool operator == ( T * n ) const {
return name == n -> name;
}
};
vector<T *> t;
t.push_back(new T("one"));
t.push_back(new T("two"));
t.push_back(new T("three"));
bool has_3 = binary_search( t.begin(), t.end(), new T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
The comparation function should be just fine yet when i run the code has_3 equals to 0 = the element isnt present in vector. Is this problem caused by my overloading of < ? I see no reason why this shouldnt find the value. Considering the order of insertion into vector it should be sorted
Thanks for help.
There are several reasons why this shouldn't find the value:
The range must be sorted; your range is out of alphabetical order
Your comparison functionality is defined between T and T*, while you search a vector of T* for a T*.
You can fix the first problem by swapping "two" and "three", and the second problem by making a vector of T:
struct T{
string name;
T(string n):name(n){};
bool operator < ( const T &n ) const {
return name < n.name;
}
// operator == is not necessary for binary_search
};
int main() {
vector<T> t;
t.push_back(T("one"));
t.push_back(T("three"));
t.push_back(T("two"));
bool has_3 = binary_search( t.begin(), t.end(), T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
return 0;
}
Demo 1.
If you do have no way but to construct a vector of pointers, you have this ugly work-around available (I strongly recommend against it):
struct T{
string name;
T(string n):name(n){};
};
bool operator < (const T& l, const T *r) {
return l.name < r->name;
}
bool operator < (const T *l, const T &r) {
return l->name < r.name;
}
Now you can search like this:
bool has_3 = binary_search( t.begin(), t.end(), T("two") ) ;
if( has_3 ){
cout <<"Its there" << endl;
}
Demo 2.
It's a really dumb requirement to work with a vector of pointers to dynamically allocated objects. But here is an approach that will work.
#include <iostream>
#include <string>
#include <algorithm>
struct T
{
std::string name;
T(std::string n):name(n){};
};
// this is the comparater needed to work with pointers, but it should
// NOT be a member of T
bool pointer_comparer(const T *left, const T *right)
{
// this assumes both left and right point to valid objects
return left->name < right->name;
}
int main()
{
std::vector<T *> t;
t.push_back(new T("one"));
t.push_back(new T("two"));
t.push_back(new T("three"));
// t is unsorted. We need to sort it since binary_search will
// ASSUME it is sorted
std::sort(t.begin(), t.end(), pointer_comparer);
T *value_needed = new T("two");
bool has_3 = std::binary_search( t.begin(), t.end(), value_needed, pointer_comparer);
if(has_3)
{
std::cout <<"Its there" << std::endl;
}
// since we've been stupidly allocating objects, we need to release them
delete value_needed;
for (std::vector<T *>::iterator i = t.begin(), end = t.end();
i != end; ++i)
{
delete (*i);
}
// and since t now contains a set of dangling pointers, we need to discard them too
t.resize(0);
return 0;
}
Why do I say the requirement to work with a vector of pointers to dynamically allocated objects. Compare the above with an approach that works with a vector<T> rather than a vector<T *>.
#include <iostream>
#include <string>
#include <algorithm>
struct T
{
std::string name;
T(std::string n):name(n){};
bool operator < (const T &) const
{
return name < n.name;
};
};
int main()
{
std::vector<T> t;
t.push_back(T("one"));
t.push_back(T("two"));
t.push_back(T("three"));
// t is unsorted. We need to sort it since binary_search will
// ASSUME it is sorted
std::sort(t.begin(), t.end());
bool has_3 = std::binary_search(t.begin(), t.end(), T("two"));
if(has_3)
{
std::cout <<"Its there" << std::endl;
}
// we need do nothing here. All objects use above will be properly released
return 0;
}
Note: I've written the above so it works with ALL C++ standards. Assuming C++11 and later, simplifications are possible in both cases.

Use std::find on vector<class> to find matching class attribute [duplicate]

Again me with vectors. I hope I'm not too annoying. I have a struct like this :
struct monster
{
DWORD id;
int x;
int y;
int distance;
int HP;
};
So I created a vector :
std::vector<monster> monsters;
But now I don't know how to search through the vector. I want to find an ID of the monster inside the vector.
DWORD monster = 0xFFFAAA;
it = std::find(bot.monsters.begin(), bot.monsters.end(), currentMonster);
But obviously it doesn't work. I want to iterate only through the .id element of the struct, and I don't know how to do that. Help is greatly appreciated. Thanks !
std::find_if:
it = std::find_if(bot.monsters.begin(), bot.monsters.end(),
boost::bind(&monster::id, _1) == currentMonster);
Or write your own function object if you don't have boost. Would look like this
struct find_id : std::unary_function<monster, bool> {
DWORD id;
find_id(DWORD id):id(id) { }
bool operator()(monster const& m) const {
return m.id == id;
}
};
it = std::find_if(bot.monsters.begin(), bot.monsters.end(),
find_id(currentMonster));
how about:
std::find_if(monsters.begin(),
monsters.end(),
[&cm = currentMonster]
(const monster& m) -> bool { return cm == m; });
You need to write your own search predicate:
struct find_monster
{
DWORD id;
find_monster(DWORD id) : id(id) {}
bool operator () ( const monster& m ) const
{
return m.id == id;
}
};
it = std::find_if( monsters.begin(), monsters.end(), find_monster(monsterID));
Take a look at the std::find template, the third parameter especially:
template<class InputIterator, class EqualityComparable>
InputIterator find(InputIterator first, InputIterator last,
const EqualityComparable& value);
What is this EqualityComparable? Again from the documentation:
A type is EqualityComparable if objects of that type can be
compared for equality using operator==, and if operator== is
an equivalence relation.
Now, your type monster needs to define such an operator. If you don't the compiler generates one for you (as also the default ctor and the dtor) which does a memcmp sort of thing which doesn't work in your case. So, to use std::find first define a comparator function/functor that the algorithm can use to match your currentMonster i.e. something along the lines of:
struct monster {
// members
bool operator==(const monster& l, const monster& r) const
{
return l.id == r.id;
}
};
or put the monsters in a map instead of a vector
or if they must be in a vector create an index map ie map of ID to vector index
This is a complete sample based on the answer of Johannes Schaub (boost version).
#include <algorithm>
#include <boost/bind.hpp>
struct monster
{
DWORD id;
int x;
int y;
int distance;
int HP;
};
int main ()
{
std::vector<monster> monsters;
monster newMonster;
newMonster.id = 1;
newMonster.x = 10;
monsters.push_back ( newMonster );
newMonster.id = 2;
newMonster.x = 20;
monsters.push_back ( newMonster );
newMonster.id = 2;
newMonster.x = 30;
monsters.push_back ( newMonster );
DWORD monsterId = 2;
std::vector< monster >::iterator it = std::find_if ( monsters.begin (), monsters.end (),
boost::bind ( &monster::id, _1 ) == monsterId );
return 0;
}
You can write a function as below:
monster* findMonster(DWORD currentMonster) {
for (auto it = bot.monsters.begin(); it != bot.monsters.end(); it++) {
if (it->id == currentMonster) {
return &(*it);
}
}
return NULL;
}
It returns a pointer to the stored node if it's found in the vector, otherwise returns NULL.
Please note that return it; won't work directly.

How to find an instance of a class with specific member values in a std::vector [duplicate]

Again me with vectors. I hope I'm not too annoying. I have a struct like this :
struct monster
{
DWORD id;
int x;
int y;
int distance;
int HP;
};
So I created a vector :
std::vector<monster> monsters;
But now I don't know how to search through the vector. I want to find an ID of the monster inside the vector.
DWORD monster = 0xFFFAAA;
it = std::find(bot.monsters.begin(), bot.monsters.end(), currentMonster);
But obviously it doesn't work. I want to iterate only through the .id element of the struct, and I don't know how to do that. Help is greatly appreciated. Thanks !
std::find_if:
it = std::find_if(bot.monsters.begin(), bot.monsters.end(),
boost::bind(&monster::id, _1) == currentMonster);
Or write your own function object if you don't have boost. Would look like this
struct find_id : std::unary_function<monster, bool> {
DWORD id;
find_id(DWORD id):id(id) { }
bool operator()(monster const& m) const {
return m.id == id;
}
};
it = std::find_if(bot.monsters.begin(), bot.monsters.end(),
find_id(currentMonster));
how about:
std::find_if(monsters.begin(),
monsters.end(),
[&cm = currentMonster]
(const monster& m) -> bool { return cm == m; });
You need to write your own search predicate:
struct find_monster
{
DWORD id;
find_monster(DWORD id) : id(id) {}
bool operator () ( const monster& m ) const
{
return m.id == id;
}
};
it = std::find_if( monsters.begin(), monsters.end(), find_monster(monsterID));
Take a look at the std::find template, the third parameter especially:
template<class InputIterator, class EqualityComparable>
InputIterator find(InputIterator first, InputIterator last,
const EqualityComparable& value);
What is this EqualityComparable? Again from the documentation:
A type is EqualityComparable if objects of that type can be
compared for equality using operator==, and if operator== is
an equivalence relation.
Now, your type monster needs to define such an operator. If you don't the compiler generates one for you (as also the default ctor and the dtor) which does a memcmp sort of thing which doesn't work in your case. So, to use std::find first define a comparator function/functor that the algorithm can use to match your currentMonster i.e. something along the lines of:
struct monster {
// members
bool operator==(const monster& l, const monster& r) const
{
return l.id == r.id;
}
};
or put the monsters in a map instead of a vector
or if they must be in a vector create an index map ie map of ID to vector index
This is a complete sample based on the answer of Johannes Schaub (boost version).
#include <algorithm>
#include <boost/bind.hpp>
struct monster
{
DWORD id;
int x;
int y;
int distance;
int HP;
};
int main ()
{
std::vector<monster> monsters;
monster newMonster;
newMonster.id = 1;
newMonster.x = 10;
monsters.push_back ( newMonster );
newMonster.id = 2;
newMonster.x = 20;
monsters.push_back ( newMonster );
newMonster.id = 2;
newMonster.x = 30;
monsters.push_back ( newMonster );
DWORD monsterId = 2;
std::vector< monster >::iterator it = std::find_if ( monsters.begin (), monsters.end (),
boost::bind ( &monster::id, _1 ) == monsterId );
return 0;
}
You can write a function as below:
monster* findMonster(DWORD currentMonster) {
for (auto it = bot.monsters.begin(); it != bot.monsters.end(); it++) {
if (it->id == currentMonster) {
return &(*it);
}
}
return NULL;
}
It returns a pointer to the stored node if it's found in the vector, otherwise returns NULL.
Please note that return it; won't work directly.

Syntax for finding structs in multisets - C++

I can't seem to figure out the syntax for finding structs in containers.
I have a multiset of Event structs. I'm trying to find one of these structs by searching on its key. I get the compiler error commented below.
struct Event {
public:
bool operator < ( const Event & rhs ) const {
return ( time < rhs.time );
}
bool operator > ( const Event & rhs ) const {
return ( time > rhs.time );
}
bool operator == ( const Event & rhs ) const {
return ( time == rhs.time );
}
double time;
int eventID;
int hostID;
int s;
};
typedef std::multiset< Event, std::less< Event > > EventPQ;
EventPQ currentEvents;
double oldRecTime = 20.0;
EventPQ::iterator ceItr = currentEvents.find( EventPQ::key_type( oldRecTime ) ); // no matching function call
I've tried a few permutations to no avail. I thought defining the conditional equality operator was going to be enough.
Solution
After correcting my typo (sorry), I now have a solution closest to AraK's, augmented by Soapbox's suggested use of explicit:
struct Event {
explicit Event(double t) : time(t), eventID(), hostID(), s() {}
Event(double t, int eid, int hid, int stype) : time(t), eventID( eid ), hostID( hid ), s(stype) {}
...
};
EventPQ::iterator ceItr = currentEvents.find( EventPQ::key_type( Event(oldRecTime) ) );
I recently discovered that another option would have been to use find_if, discussed here.
Thanks for the help.
You don't have a suitable constructor that accepts double. Just add the following constructor:
Event(double t) : time(t), eventID(/**/), hostIDeventID(/**/), s(/**/)
{ }
Here is how Event would look like:
struct Event {
public:
// Initialize other variables as needed
Event(double t) : time(t), eventID(/**/), hostIDeventID(/**/), s(/**/)
{ }
bool operator < ( const Event & rhs ) const {
return ( time < rhs.time );
}
bool operator > ( const Event & rhs ) const {
return ( time > rhs.time );
}
bool operator == ( const Event & rhs ) const {
return ( time == rhs.time );
}
double time;
int eventID;
int hostID;
int s;
};
// No need for std::less because it is used by default,
// when you define 'operator <' in your class
typedef std::multiset< Event > EventPQ;
EventPQ currentEvents;
double oldRecTime = 20.0;
// You can just pass the double, a temporary object will be created
// for you.
EventPQ::iterator ceItr = currentEvents.find( oldRecTime );
Besides the missing constructor, you don't want to call find() on the iterator ceItr but on currentEvents:
EventPQ::iterator ceItr = currentEvents.find(EventPQ::key_type(oldRecTime));
Note that find() only gives you an iterator to the first match, use equal_range() to get a range of all matches:
std::pair<EventPQ::iterator, EventPQ::iterator> result;
result = currentEvents.find(EventPQ::key_type(oldRecTime));
for(EventPQ::iterator it = result.first; it != result.second; ++it) {
// do stuff
}
You seem to have multiset and multimap confused. Multiset is for when the key and the value are one-and-the-same. Multimap is for when a key and a value are associated, but not the same object.
In this case, Event isn't actually the key. The "time" double appears to be the key. Since the key and the value are not exactly the same, you should use a multimap. Using event as both the key and value doesn't make sense here.
You don't want to construct an event with extra fields you don't need just to search for a given value. Instead, multimap lets you search using the double, which is what you really want. This also eliminates the need for less than operators in the Event class.
Your code would then look something like this:
struct Event {
double time;
int eventID;
int hostID;
int s;
};
typedef std::multimap<double, Event> EventPQ;
EventPQ currentEvents;
double oldRecTime = 20.0;
std::pair<EventPQ::iterator, EventPQ::iterator> results = currentEvents.equal_range(oldRecTime);
for(EventPQ::iterator cur = results.first; cur != results.second; ++cur) {
// do something to *cur
}