How to find an object with specific field values in a std::set?
This depends on the implementation of T
. Let's stick with your example of a class Car
. Suppose that class looks something like this:
class Car {
public:
Car(std::string color, unsigned int number_of_doors,
unsigned int top_speed);
// getters for all these attributes
// implementation of operator< as required for std::set
};
The operator<
should order instances of Car
based on all attributes in order to make searching for all attributes possible. Otherwise you will get incorrect results.
So basically, you can construct an instance of car using just these attributes. In that case, you can use std::set::find
and supply a temporary instance of Car
with the attributes you are looking for:
car_set.find(Car("green", 4, 120));
If you want to search for an instance of Car
specifying only a subset of its attributes, like all green cars, you can use std::find_if
with a custom predicate:
struct find_by_color {
find_by_color(const std::string & color) : color(color) {}
bool operator()(const Car & car) {
return car.color == color;
}
private:
std::string color;
};
// in your code
std::set<Car>::iterator result = std::find_if(cars.begin(), cars.end(),
find_by_color("green"));
if(result != cars.end()) {
// we found something
}
else {
// no match
}
Note that the second solution has linear complexity, because it cannot rely on any ordering that may or may not exists for the predicate you use. The first solution however has logarithmic complexity, as it can benefit from the order of an std::set
.
If, as suggested by @Betas comment on your question, you want to compose the predicates at runtime, you would have to write some helper-classes to compose different predicates.
Search a vector of objects by object attribute
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));
Find elements in stl::setobject based on member variable of object
You can do it using boost::multi_index_container
class myObject
{
public:
int a;
int b;
int c;
bool operator < (const myObject& obj) const
{ return b < obj.b; }
};
using namespace boost::multi_index;
typedef multi_index_container<
myObject,
indexed_by<
ordered_unique<identity<myObject> >, // index by operator <
ordered_unique<member<myObject, int, &myObject::a> > // index by member a
>
> SetOfmyObjects;
typedef SetOfmyObjects::nth_index<0>::type SetIndex_b;
typedef SetOfmyObjects::nth_index<1>::type SetIndex_a;
...
SetOfmyObjects s;
const SetIndex_b& index_b = s.get<0>();
SetIndex_b::iterator it_b;
for (it_b = index_b.begin(); it_b != index_b.end(); ++it_b)
// ordered by b
const SetIndex_a& index_a = s.get<1>();
SetIndex_a::iterator it_a;
for (it_a = index_a.begin(); it_a != index_a.end(); ++it_a)
// ordered by a
find a value in a vector of class objects
I found two solution as for my problem:
You can implement std::find in vector class-based object in c++:
class A {
private:
string b;
string c;
public:
A(string i) : b(i) {}
A(string n, string l) { b = n ;c = l; }
string Getb(){ return b; }
string Getc(){ return c; }
bool operator==(const A & obj2) const
{
return (this->b.compare(obj2.b) == 0);
}
};
int main()
{
vector<A> a1;
a1.push_back(A("AA","aa"));
a1.push_back(A("BB","bb"));
a1.push_back(A("CC","cc"));
a1.push_back(A("DD","dd"));
auto it = find(a1.begin(), a1.end(), A("CC"));
if (it != a1.end()) {
auto idx = distance(a1.begin(), it);
cout << "b= " << it->Getb() << " c= " << it->Getc() << endl;
cout << "Index= " << idx << endl;
} else
cout << "CC is not found" << endl;
return 0;
}You can implement std::find_if in vector class/structure based object in c++ (thanks to @Vlad from Moscow and @R Sahu):
class A {
private:
string b;
string c;
public:
A(string n, string l) { b = n ;c = l; }
string Getb(){ return b; }
string Getc(){ return c; }
struct Finder {
Finder(string const & n) : name(n) { }
bool operator () (const A & el) const {
return el.Pos == name;
}
string name;
};
};
int main()
{
vector<A> a1;
a1.push_back(A("AA","aa"));
a1.push_back(A("BB","bb"));
a1.push_back(A("CC","cc"));
a1.push_back(A("DD","dd"));
vector<A>::iterator it;
it = find_if(a1.begin(), a1.end(), A::Finder ("CC"));
if (it != a1.end()) {
auto idx = distance(a1.begin(), it);
cout << "b= " << it->Getb() << " c= " << it->Getc() << endl;
cout << "Index= " << idx << endl;
} else
cout << "CC is not found" << endl;
return 0;
}
c++ set::find by object property
You can use the 4-parameter overload of std::equal_range, providing a comparator that compares a Property
with an Object
.
struct Cmp
{
bool operator() ( const Object& o, const Property& p ) const
{
return o.property < p;
}
bool operator() ( const Property& p, const Object& o ) const
{
return p < o.property;
}
};
std::set<Object> s = ....;
Property property_val = ....;
auto r = std::equal_range(s.begin(),s.end(), property_val, Cmp());
Another option is to use std::find_if
with a suitable unary predicate. But you wouldn't benefit from the logarithmic lookup of std::set
or std::equal_range
.
How to find out if an item is present in a std::vector?
You can use std::find
from <algorithm>
:
#include <algorithm>
#include <vector>
vector<int> vec;
//can have other data types instead of int but must same datatype as item
std::find(vec.begin(), vec.end(), item) != vec.end()
This returns an iterator to the first element found. If not present, it returns an iterator to one-past-the-end. With your example:
#include <algorithm>
#include <vector>
if ( std::find(vec.begin(), vec.end(), item) != vec.end() )
do_this();
else
do_that();
Find elements of std::set by custom comparison with value of different type
With C++14 you can utilize "transparent" comparator:
#include <iostream>
#include <set>
#include <type_traits>
class A
{
public: explicit A() : a{cnt++} {}
private: explicit A(int) = delete;
public: const int a;
private: static int cnt;
};
int A::cnt{};
class Comparator
{
// this member is required to let container be aware that
// comparator is capable of dealing with types other than key
public: using is_transparent = std::true_type;
public: bool operator()(const int & left, const A& right) const
{
return left < right.a;
}
public: bool operator()(const A & left, const int& right) const
{
return left.a < right;
}
public: bool operator()(const A& left, const A& right) const
{
return left.a < right.a;
}
};
int main()
{
std::set<A, Comparator> sa{};
for (int i{}; i < 10; ++i)
{
sa.emplace();
}
std::cout << sa.find(3)->a << std::endl;
return 0;
}
online compiler
Before C++14 heterogenous lookup was available in ::boost::intrusive::set
:
#include <boost/intrusive/set.hpp>
#include <iostream>
namespace bi = ::boost::intrusive;
// hook contains set node data, supports various options, can be a member
class A: public bi::set_base_hook
<
bi::link_mode<bi::link_mode_type::safe_link>
>
{
public: explicit A() : a{cnt++} {}
private: explicit A(int) = delete;
public: const int a;
private: static int cnt;
};
int A::cnt{};
class Comparator
{
public: bool operator()(const int & left, const A& right) const
{
return left < right.a;
}
public: bool operator()(const A & left, const int& right) const
{
return left.a < right;
}
public: bool operator()(const A& left, const A& right) const
{
return left.a < right.a;
}
};
int main()
{
bi::set<A, bi::compare<Comparator>> sa{Comparator{}};
for (int i{0}; i < 10; ++i)
{
sa.insert(*new A{}); // typically user manages object creation
}
// comparators may vary
std::cout << sa.find(3, Comparator{})->a << std::endl;
return 0;
}
online compiler
Related Topics
Given a Start and End Point, and a Distance, Calculate a Point Along a Line
Advantages of Using Arrays Instead of Std::Vector
R: C++ Optimization Flag When Using the Inline Package
Programmatically Getting Per-Process Disk Io Statistics on Windows
Multithreading on Dual Core MAChine
Understanding Y Combinator Through Generic Lambdas
Absence of Typeof Operator in C++03
C++ Connect Output Stream to Input Stream
How to Control Memory Allocation Strategy in Third Party Library Code
Read and Write on Serial Port in Ubuntu with C/C++ and Libserial
Std::Lower_Bound Slower for Std::Vector Than Std::Map::Find