Harold Huang
Harold Huang

Reputation: 3

In C++, how to find certain items in the vector of objects by giving the member of a object?

For example, I have defined the class foo and the vector vec:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class foo {
public:
    foo(int flag, char ch):flag(flag), ch(ch) {}
    int flag;
    char ch;
};
int main(void)
{
    vector<foo> vec;
    vec.push_back(foo(1,'a'));
    vec.push_back(foo(2,'b'));
    vec.push_back(foo(3,'c'));
    //blabla...
}

I've found how to find the single element: How to find an item in a std::vector?
But now I want to find an object by just giving a char, say, 'b'. How can I achieve this goal efficiently?

Upvotes: 0

Views: 121

Answers (4)

Shreevardhan
Shreevardhan

Reputation: 12651

Define an implicit constructor for foo using only char as argument.

foo(char ch) : ch(ch), flag(int()) {
}

OR add default args to your existing constructor

foo(char ch, int flag = int()) : ch(ch), flag(flag) {
}

AND overload a comparison operator

bool operator ==(const char & rhs) {
    return ch == rhs;
}

You can then use std::find directly.

vector<foo>::iterator it = std::find(vec.begin(), vec.end(), 'b');
if (it != vec.end()) {
    ...    // do something
}

A working code will then look like

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class foo {
public:
    foo(char ch, int flag = int()) : flag(flag), ch(ch) {}
    int flag;
    char ch;
    inline bool operator ==(const char & rhs) { return ch == rhs; }
};

int main(void) {
    vector<foo> vec;
    vec.push_back(('a', 1));
    vec.push_back(('b', 2));
    vec.push_back(('c', 3));
    vector<foo>::iterator it = find(vec.begin(), vec.end(), 'b');
    if (it != vec.end())
        cout << "found" << endl;
    return 0;
}

See http://ideone.com/ue907i demo.

Upvotes: 1

TartanLlama
TartanLlama

Reputation: 65770

You could use std::find_if for this:

 //c++14
 std::find_if(std::begin(vec),std::end(vec),
              [](auto&& v) { return v.ch == 'b'; })

 //c++11
 std::find_if(std::begin(vec),std::end(vec),
              [](const foo& v) { return v.ch == 'b'; })

You could wrap this up in a function if you find yourself needing this pattern a number of times:

//c++14, c++11 version left as exercise
decltype(auto) find_foo (const std::vector<foo>& vec, char c)
{
    return std::find_if(std::begin(vec),std::end(vec),
                        [c](auto&& v) { return v.ch == c; });
}

A better option might be to use a std::unordered_map<char,int> instead of a std::vector<foo>:

int main()
{
    std::unordered_map<char,int> my_map;
    my_map['a'] = 1;
    my_map['b'] = 2;
    my_map['c'] = 3;

    cout << my_map['b']; //instead of that std::find_if nonsense
}

Upvotes: 6

Vlad from Moscow
Vlad from Moscow

Reputation: 311126

You can use standard algorithm std::find_if declared in header <algorithm>. For example

#include <iostream>
#include <algorithm>

class foo {
public:
    foo(int flag, char ch):flag(flag), ch(ch) {}
    int flag;
    char ch;
};

int main()
{
    std::vector<foo> vec;

    vec.push_back(foo(1,'a'));
    vec.push_back(foo(2,'b'));
    vec.push_back(foo(3,'c'));

    char ch = 'b';
    auto it = std::find_if( vec.begin(), vec.end(), 
                            [&]( const foo &f ) { return f.ch == ch; } );

    if ( it != vec.end() ) std::cout << it->flag << ' ' << it->ch << std::endl;

}    

If your compiler supports C++14 then the lambda expression can be written also the following way

auto it = std::find_if( vec.begin(), vec.end(), 
                        [ch = 'b']( const foo &f ) { return f.ch == ch; } );

The other approach is to make the seach as an interface of the class itself. For example

class foo {
public:
    foo(int flag, char ch):flag(flag), ch(ch) {}
    int flag;
    char ch;
    class find_by_ch
    {
    public:        
        find_by_ch( char ch ) : ch( ch ) {}
        bool operator ()( const foo &f ) const { return f.ch == ch; }
    private:
        char ch;
    };        
};

//...

auto it = std::find_if( vec.begin(), vec.end(), foo::find_by_ch( 'b' ) );

Upvotes: 2

fluffybunny
fluffybunny

Reputation: 506

If the vector is sorted (as in your example), you can use lower_bound for logarithmic performance. If the vector is not sorted, you can use find for linear performance.

Upvotes: 0

Related Questions