How to Find an Object with Specific Field Values in a Std::Set

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:

  1. 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;
    }
  2. 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



Leave a reply



Submit