Search a vector of objects by object attribute

60,414

Solution 1

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));

Solution 2

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.

Solution 3

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
}
Share:
60,414

Related videos on Youtube

Christoffer
Author by

Christoffer

Freelance programmer. Mostly in backend.

Updated on July 09, 2022

Comments

  • Christoffer
    Christoffer almost 2 years

    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.

    • Peter Wood
      Peter Wood about 11 years
      Can there be more than one object with the same name? Would you want to find all of them?
    • Darnoc Eloc
      Darnoc Eloc almost 4 years
      How would one search within two containers of objects for matching member variables?
  • Rameez Rami
    Rameez Rami about 11 years
    You need to use find_if if you're using a functor.
  • gman
    gman almost 6 years
    shouldn't it have ++itr in the while loop?
  • Darnoc Eloc
    Darnoc Eloc almost 4 years
    How would one search within two containers of objects for matching member variables?