web-dev-qa-db-fra.com

Rechercher un vecteur d'objets par attribut d'objet

J'essaie de trouver une belle façon de trouver l'index d'un certain objet dans un vecteur - en comparant une chaîne à un champ membre dans l'objet.

Comme ça:

find(vector.begin(), vector.end(), [object where obj.getName() == myString])

J'ai cherché sans succès - peut-être que je ne comprends pas vraiment quoi chercher.

32
Christoffer

Vous pouvez utiliser std::find_if avec un foncteur approprié. Dans cet exemple, un lambda C++ 11 est utilisé:

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

Si vous n'avez pas de support lambda C++ 11, un foncteur fonctionnerait:

struct MatchString
{
 MatchString(const std::string& s) : s_(s) {}
 bool operator()(const Type& obj) const
 {
   return obj.getName() == s_;
 }
 private:
   const std::string& s_;
};

Ici, MatchString est un type dont les instances sont appelables avec un seul objet Type et renvoient un booléen. Par exemple,

Type t("Foo"); // assume this means t.getName() is "Foo"
MatchString m("Foo");
bool b = m(t); // b is true

alors vous pouvez passer une instance à std::find

std::vector<Type>::iterator it = find_if(v.begin(), v.end(), MatchString(myString));
53
juanchopanza

En plus du Lambda et du foncteur manuscrit utilisé par juancho, vous avez la possibilité d'utiliser boost::bind (C++ 03) ou std::bind (C++ 11) et une fonction simple:

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

Ou, si Type a une méthode isName:

std::vector<Type>::iterator it = find_if(v.begin(), v.end(), 
  boost::bind(&Type::isName, boost::placeholders::_1, myString));

C'est juste pour être complet. En C++ 11, je préférerais Lambdas, en C++ 03, j'utiliserais bind uniquement si la fonction de comparaison elle-même existe déjà. Sinon, préférez le foncteur.

PS: Puisque C++ 11 n'a pas de lambdas polymorphes/basés sur des modèles, la liaison a toujours sa place dans C++ 11, par ex. si les types de paramètres sont inconnus, difficiles à épeler ou autrement difficiles à déduire.

6
Arne Mertz

Un simple itérateur peut vous aider.

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
}
3
uniqrish