Finding in a std::vector of structures - c++

I have:
struct MyStruct
{
char* name;
int* somethingElse;
};
And I need to find in a std::vector<MyStruct*> an element (by using std::find_if) whose name is "XYZ" ... but ... the Predicate of std::find_if (if I have managed to understand it correctly) is a plain function, and it takes in a MyStruct pointer and I have no idea where I can specify the extra "XYZ" value to be used in the comparison.
So, how can I use std::find_if or this purpose? (Obviously, looking for a nice solution, not a global variable, or just walk through the list, ....)
Thanks, f

You can use a functor for this (hope I didn't get anything wrong, as I typed it in the browser):
class finder
{
const char* name;
public:
finder(const char* _name): name(_name) {}
bool operator()(MyStruct* elem) {return strcmp(elem->name, name) == 0;}
};
finder f("sample");
std::find_if(myvector.begin(), myvector.end(), f);

If you use C++11 and lambda:
std::vector<MyStruct> mystructus;
std::find_if(mystructus.begin(), mystructus.end(),
[](const MyStruct& ms){ return ms.name == std::string("XYZ"); } );

You have two options, either use functors or lamdas.
Using functors, you create a new class (or structure) whose constructor takes the string you want to search for, then it has an operator() function that is called by std::find_if:
class my_finder
{
std::string search;
public:
my_finder(const std::string& str)
: search(str)
{}
bool operator()(const MyStruct* my_struct) const
{ return search == my_struct->name; }
};
// ...
std::find_if(std::begin(...), std::end(...), my_finder("XYZ"));
The second using lambdas is less code, but requires recent version of the compiler that can handle C++11 lambdas:
std::find_if(std::begin(...), std::end(...), [](const MyStruct* my_struct)
{ return std::string("XYZ") == my_struct->name; });
The last example can even be generalized further:
using namespace std::placeholders; // For `_1` used below in `std::bind`
// Declare a "finder" function, to find your structure
auto finder = [](const MyStruct* my_struct, const std::string& to_find) {
return to_find == my_struct->name;
};
auto xyz = std::find_if(std::begin(...), std::end(...), std::bind(finder, _1, "XYZ"));
auto abc = std::find_if(std::begin(...), std::end(...), std::bind(finder, _1, "ABC"));
This way the lambda can be reused.

Predicate is anything, that can have operator () applied to it (with the expected argument(s) and returns something convertible to bool). A pointer to function is such thing, but so is an object that defines operator().

You need to provide a predicate like this:
struct Comparator
{
Comparator(const char* find) : m_find(find){}
bool operator()(MyStruct* p) const
{
return strcmp(p->name, m_find) == 0;
}
const char* m_find;
};
Then you can std::find_if like this:
vector<MyStruct*>::iterator iter = std::find_if(vec.begin(), vec.end(), Comparator("XYZ"));
if(iter != vec.end())
{
MyStruct* p = *iter;
}
Or if your compiler supports C++11 you can use lambdas and get rid of the predicate functor:
auto it = std::find_if(vec.begin(), vec.end(), [](MyStruct* p) { return strcmp(p->name, "XYZ") == 0;});

Related

How to retreive a specific element from a vector and assign it to a variable? [duplicate]

I'm trying to figure out a nice way to find the index of a certain object in a vector - by comparing a string to a member field in the object.
Like this:
find(vector.begin(), vector.end(), [object where obj.getName() == myString])
I have searched without success - maybe I don't fully understand what to look for.
You can use std::find_if with a suitable functor. In this example, a C++11 lambda is used:
std::vector<Type> v = ....;
std::string myString = ....;
auto it = find_if(v.begin(), v.end(), [&myString](const Type& obj) {return obj.getName() == myString;})
if (it != v.end())
{
// found element. it is an iterator to the first matching element.
// if you really need the index, you can also get it:
auto index = std::distance(v.begin(), it);
}
If you have no C++11 lambda support, a functor would work:
struct MatchString
{
MatchString(const std::string& s) : s_(s) {}
bool operator()(const Type& obj) const
{
return obj.getName() == s_;
}
private:
const std::string& s_;
};
Here, MatchString is a type whose instances are callable with a single Type object, and return a boolean. For example,
Type t("Foo"); // assume this means t.getName() is "Foo"
MatchString m("Foo");
bool b = m(t); // b is true
then you can pass an instance to std::find
std::vector<Type>::iterator it = find_if(v.begin(), v.end(), MatchString(myString));
In addition to the Lambda and the handwritten functor used by juancho, you have the possibility to use boost::bind (C++03) or std::bind (C++11) and a simple function:
bool isNameOfObj(const std::string& s, const Type& obj)
{ return obj.getName() == s; }
//...
std::vector<Type>::iterator it = find_if(v.begin(), v.end(),
boost::bind(&isNameOfObj, myString, boost::placeholders::_1));
Or, if Type has a method isName:
std::vector<Type>::iterator it = find_if(v.begin(), v.end(),
boost::bind(&Type::isName, boost::placeholders::_1, myString));
This is just for completeness. In C++11 I'd prefer Lambdas, in C++03 I'd use bind only if the comparison function itself exists already. If not, prefer the functor.
PS: Since C++11 has no polymorphic/templated lambdas, bind still has it's place in C++11, e.g. if the parameter types are unknown, hard to spell, or otherwise not easy to deduce.
A simple iterator may help.
typedef std::vector<MyDataType> MyDataTypeList;
// MyDataType findIt should have been defined and assigned
MyDataTypeList m_MyObjects;
//By this time, the push_back() calls should have happened
MyDataTypeList::iterator itr = m_MyObjects.begin();
while (itr != m_MyObjects.end())
{
if(m_MyObjects[*itr] == findIt) // any other comparator you may want to use
// do what ever you like
}

Can a map's key be shared with part of the value?

Can a std::map's or std::unordered_map's key be shared with part of the value? Especially if the key is non-trivial, say like a std::string?
As a simple example let's take a Person object:
struct Person {
// lots of other values
std::string name;
}
std::unordered_map<std::string, std::shared_ptr<Person>> people;
void insertPerson(std::shared_ptr<Person>& p) {
people[p.name] = p;
// ^^^^^^
// copy of name string
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
return people[name];
}
My first thought is a wrapper around the name that points to the person, but I cannot figure out how to do a lookup by name.
For your purpose, a std::map can be considered a std::set containing std::pair's which is ordered (and thus efficiently accessible) according to the first element of the pair.
This view is particularly useful if key and value elements are partly identical, because then you do not need to artificially separate value and key elements for a set (and neither you need to write wrappers around the values which select the key).
Instead, one only has to provide a custom ordering function which works on the set and extracts the relevant key part.
Following this idea, your example becomes
auto set_order = [](auto const& p, auto const& s) { return p->name < s->name; };
std::set<std::shared_ptr<Person>, decltype(set_order)> people(set_order);
void insertPerson(std::shared_ptr<Person>& p) {
people.insert(p);
}
As an alternative, here you could also drop the custom comparison and order the set by the addresses in the shared pointer (which supports < and thus can be used directly in the set):
std::set<std::shared_ptr<Person> > people;
void insertPerson(std::shared_ptr<Person>& p) {
people.insert(p);
}
Replace set by unordered_set where needed (in general you then also need to provide a suitable hash function).
EDIT: The lookup can be performed using std:lower_bound:
std::shared_ptr<Person> lookupPerson(std::string const& s)
{
auto comp = [](auto const& p, auto const& s) { return p->name < s; };
return *std::lower_bound(std::begin(people), std::end(people), s, comp);
}
DEMO.
EDIT 2: However, given this more-or-less ugly stuff, you can also follow the lines of your primary idea and use a small wrapper around the value as key, something like
struct PersonKey
{
PersonKey(std::shared_ptr<Person> const& p) : s(p->name) {}
PersonKey(std::string const& _s) : s(_s) {}
std::string s;
bool operator<(PersonKey const& rhs) const
{
return s < rhs.s;
}
};
Use it like (untested)
std::map<PersonKey, std::shared_ptr<Person> > m;
auto sptr = std::make_shared<Person>("Peter");
m[PersonKey(sptr)]=sptr;
Lookup is done through
m[PersonKey("Peter")];
Now I like this better than my first suggestion ;-)
Here's an alternative to davidhigh's answer.
struct Person {
// lots of other values
std::string name;
}
struct StrPtrCmp {
bool operator()(const std::string* a, const std::string* b) const {
return *a < *b;
}
}
std::map<const std::string*, std::shared_ptr<Person>, StrPtrCmp> people();
void insertPerson(std::shared_ptr<Person>& p) {
people[&(p.name)] = p;
}
std::shared_ptr<Person> lookupPerson(const std::string& name) const {
return people[&name];
}
And a few edits to make it work with std::unordered_map:
struct StrPtrHash {
size_t operator()(const std::string* p) const {
return std::hash<std::string>()(*p);
}
};
struct StrPtrEquality {
bool operator()(const std::string* a, const std::string* b) const {
return std::equal_to<std::string>()(*a, *b);
}
};
std::unordered_map<const std::string*, std::shared_ptr<Person>, StrPtrHash, StrPtrEquality> people();

Does std::for_each pass by value or by reference?

I have a test map with pair of std::string and Person pointer
class MyMap {
public:
void clear() {
std::for_each(people.begin(), people.end(),std::bind1st(std::mem_fun(&MyMap::remove), this));
}
void remove(std::pair<std::string, Person*> p) { delete p.second; }
private:
std::map<name, Person*> people;
};
My question is does for_each pass each Person pair by ref or value? is it worth using my own, this is a bit cleaner.
In addition to that if I want to use boost::bind or std::bind (c++11) instead of bind1st, how would I do it? should this function be like struct having operator() that inherits std::unary_function?
The type of the map is std::map<name, Person*>, but the parameter for the remove function is std::pair<std::string, Person*>. This won't work unless name is typedef for std::string.
The way your remove function is currently defined, you'll make copies of the map's value_type. Change the function signature to:
void remove(std::pair<const std::string, Person *>& p)
// ^^^^^ ^
// key must be const take a reference
To use std::bind instead of std::bind1st
std::for_each( people.begin(),
people.end(),
std::bind( &MyMap::remove, this, std::placeholders::_1 ) );
But if you can use C++11 features, there's no need for std::bind, lambda is much nicer.
std::for_each( people.begin(),
people.end(),
[]( decltype(*people.begin())& p ) { delete p.second; } );
or use a range based for loop
for( auto&& p : people ) {
delete p.second;
}
for_each will call the functor either by-value or by-reference, depending on how you have defined the functor.
For example:
struct Gizmo
{
bool operator() (const Zippy& rhs) const
{
// ...
}
};
This functor is call-by-ref. However:
struct Gizmo
{
bool operator() (Zippy rhs) const
{
// ...
}
};
This one is call-by-value.

Search for a struct item in a vector by member data

I'm very new to c++ and I'm trying to find a way to search a vector of structs for a struct with a certain member data.
I know this would work with simple types in the vector
std::find(vector.begin(), vector.end(), item) != vector.end()
But lets say I have a struct like this:
struct Friend
{
string name;
string number;
string ID;
};
and a vector like this:
vector<Friend> friends;
Then the vector is filled with friends.
Let's say I want to search for a friend with a certain ID, and cout the details. Or delete the certain struct from the vector. Is there a simple way to do this?
This can be done with std::find_if and a search predicate, which can be expressed as a lambda function if you have C++11 (or C++0x) available:
auto pred = [](const Friend & item) {
return item.ID == 42;
};
std::find_if(std::begin(friends), std::end(friends), pred) != std::end(friends);
To use an ID given as a variable, you have to capture it in the lambda expression (within the [...]):
auto pred = [id](const Friend & item) {
return item.ID == id;
};
std::find_if(std::begin(friends), std::end(friends), pred) != std::end(friends);
If you don't have C++11 available, you have to define the predicate as a functor (function object). Remy Lebeau's answer uses this approach.
To remove elements matching the criteria as defined by the predicate, use remove_if instead of find_if (the rest of the syntax is the same).
For more algorithms, see the STL <algorithm> reference.
Use std::find_if(). #leemes and #AndyProwl showed you how to use it in a C++11 compiler. But if you are not using a C++11 compiler, then you can use it like this instead, which defines a functor comparing the ID of a given item with a previously specified ID in its constructor:
class MatchesID
{
std::string _ID;
public:
MatchesID(const std::string &ID) : _ID(ID) {}
bool operator()(const Friend &item) const
{
return item.ID == _ID;
}
};
std::find_if(vector.begin(), vector.end(), MatchesID("TheIDHere")) != vector.end();
If you have other classes in your project which use IDs, you can make this functor templated:
template<typename IDType>
class MatchesID
{
IDType _ID;
public:
MatchesID(const IDType &ID) : _ID(ID) {}
template<class ItemType>
bool operator()(const ItemType &item) const
{
return item.ID == _ID;
}
};
std::find_if(vector.begin(), vector.end(), MatchesID<std::string>("TheIDHere")) != vector.end();
You can use std::find_if in combination with functors (if you are working with C++98) or lambdas (if you are using C++11, which I will assume):
using namespace std;
int ID = 3; // Let's say...
auto it = find_if(begin(vector), end(vector), [=] (Friend const& f) {
return (f.ID == ID);
});
bool found = (it != end(vector));
If you want to find an element in STL container, use std::find or std::find_if algorithms
With C++03, you need to overload operator== for std::find
bool operator==(const Friend& lhs, const Friend& rhs)
{
return lhs.ID == rhs.ID;
}
if (std::find(friends.begin(), friends.end(), item) != friends.end())
{
// find your friend
}
OR C++11 with lambda:
std::find_if(friends.begin(), friends.end(), [](Friend& f){ return f.ID == "1"; } );
If you want to remove a certain element, use std::remove_if
std::remove_if(friends.begin(), friends.end(),
[](Friend& f){ return f.ID == "1"; });

C++ std::find with a custom comparator

This is basically what I want to do:
bool special_compare(const string& s1, const string& s2)
{
// match with wild card
}
std::vector<string> strings;
strings.push_back("Hello");
strings.push_back("World");
// I want this to find "Hello"
find(strings.begin(), strings.end(), "hell*", special_compare);
// And I want this to find "World"
find(strings.begin(), strings.end(), "**rld", special_compare);
But std::find doesn't work like that unfortunately. So using only the STL, how can I do something like this?
Based on your comments, you're probably looking for this:
struct special_compare : public std::unary_function<std::string, bool>
{
explicit special_compare(const std::string &baseline) : baseline(baseline) {}
bool operator() (const std::string &arg)
{ return somehow_compare(arg, baseline); }
std::string baseline;
}
std::find_if(strings.begin(), strings.end(), special_compare("hell*"));
The function you need to use is this : std::find_if, because std::find doesn't take compare function.
But then std::find_if doesn't take value. You're trying to pass value and compare both, which is confusing me. Anyway, look at the documentation. See the difference of the usage:
auto it1 = std::find(strings.begin(), strings.end(), "hell*");
auto it2 = std::find_if(strings.begin(), strings.end(), special_compare);
Hope that helps.
You'll need std::find_if(), which is awkward to use, unless you're on a C++11 compiler. Because then, you don't need to hardcode the value to search for in some comparator function or implement a functor object, but can do it in a lambda expression:
vector<string> strings;
strings.push_back("Hello");
strings.push_back("World");
find_if(strings.begin(), strings.end(), [](const string& s) {
return matches_wildcard(s, "hell*");
});
Then you write a matches_wildcard() somewhere.
Since nobody has mentioned std::bind yet, I'll propose this one
#include <functional>
bool special_compare(const std::string& s, const std::string& pattern)
{
// match with wild card
}
std::vector<std::string> strings;
auto i = find_if(strings.begin(), strings.end(), std::bind(special_compare, std::placeholders::_1, "hell*"));
With C++11 lambdas:
auto found = find_if(strings.begin(), strings.end(), [] (const std::string& s) {
return /* you can use "hell*" here! */;
});
If you can't use C++11 lambdas, you can just make a function object yourself. Make a type and overload operator ().
I wanted to have an example with custom class having custom find logic but didn't find any answer like that. So I wrote this answer which uses custom comparator function (C++11) to find an object.
class Student {
private:
long long m_id;
// private fields
public:
long long getId() { return m_id; };
};
Now suppose, I want to find the student object whose m_id matches with a given id. I can write std::find_if like this:
// studentList is a vector array
long long x_id = 3; // local variable
auto itr = std::find_if(studentList.begin(), studentList.end(),
[x_id](Student& std_val)
{ return std_val.getId() == x_id; }
);
if(itr == studentList.end())
printf("nothing found");