jiyi
jiyi

Reputation: 101

How to find an item in a vector?

I have a piece of code like this.

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

class A
{
    public:
        A(int iVal) : _val(iVal) {}
        int getVal() const { return _val; }
    private:
        int _val;
};

class B
{
    public:
        B(int iNum) : _num(iNum) {}
        int getNum() const { return _num; }
    private:
        int _num;
};

bool isInVecA(vector<A> vectorA, int iVal)
{
    for(vector<A>::const_iterator it=vectorA.begin(); it != vectorA.end(); it++)
    {
        if(it->getVal() == iVal)
            return true;
    }
    return false;
}

bool isInVecB(vector<B> vectorB, int iNum)
{
    for(vector<B>::const_iterator it=vectorB.begin(); it != vectorB.end(); it++)
    {
        if(it->getNum() == iNum)
            return true;
    }
    return false;
}

int main()
{    
    A arrayA[] = { A(1), A(2), A(3) };
    vector<A> vectorA(arrayA, arrayA + sizeof(arrayA) / sizeof(A));

    B arrayB[] = { B(3), B(4), B(5) };
    vector<B> vectorB(arrayB, arrayB + sizeof(arrayB) / sizeof(B));

    int key = 3;

    if(isInVecA(vectorA, key) && isInVecB(vectorB, key))
        cout << "Key " << key << " is in both vectors." << endl;
    else
        cout << "Key " << key << " is not in both vectors." << endl;

    return 0;
}

What I want to do is just to create something to replace function isInVecA and isInVecB as they are too similar. Assume I cannot change class A and class B.


Thank you very much, everyone. I am quite new to StackOverflow. Not sure where to post a solution which my friend and I come up with. Therefore, I put it here.

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>
#include <boost/bind.hpp>

using namespace std;

class A
{
    public:
        A(int iVal) : _val(iVal) {}
        int getVal() const { return _val; }
    private:
        int _val;
};

class B
{
    public:
        B(int iNum) : _num(iNum) {}
        int getNum() const { return _num; }
    private:
        int _num;
};

template<typename T>
bool isInVec(vector<T> vec, int (T::*func)() const, int iVal)
{
    return find_if(vec.begin(), vec.end(), boost::bind(func, _1) == iVal) != vec.end();
}

int main()
{    
    A arrayA[] = { A(1), A(2), A(3) };
    vector<A> vectorA(arrayA, arrayA + sizeof(arrayA) / sizeof(A));

    B arrayB[] = { B(3), B(4), B(5) };
    vector<B> vectorB(arrayB, arrayB + sizeof(arrayB) / sizeof(B));

    int key = 3;

    if(isInVec<A>(vectorA, &A::getVal, key) && isInVec<B>(vectorB, &B::getNum, key))
        cout << "Key " << key << " is in both vectors." << endl;
    else
        cout << "Key " << key << " is not in both vectors." << endl;

    return 0;
}

Upvotes: 0

Views: 180

Answers (5)

Pixelchemist
Pixelchemist

Reputation: 24926

At some point you'll have to distinguish A and B (or more precisely getVal and getNum). For the search algorithm (as already mentioned) std::find_if can be used with the predicates being Lambdas:

#include <algorithm>

// ...

int target = 5;

auto found_A = std::find_if(vectorA.begin(), vectorA.end(), 
  [target](A const &a) -> bool { return a.getVal() == target; });

auto found_B = std::find_if(vectorB.begin(), vectorB.end(), 
  [target](B const &b) -> bool { return b.getNum() == target; });

if (found_A != vectorA.end() && found_B  != vectorB.end())
{
  // target in vA and vB 
  // with found_A and found_B being valid iterators to the elements
}

Upvotes: 0

Richard Hodges
Richard Hodges

Reputation: 69854

#include <iostream>
#include <iterator>
#include <algorithm>

// class A and B unchanged, as requested

class A
{
public:
    A(int iVal) : _val(iVal) {}
    int getVal() const { return _val; }
private:
    int _val;
};

class B
{
public:
    B(int iNum) : _num(iNum) {}
    int getNum() const { return _num; }
private:
    int _num;
};

// new predicate to cover matching

struct matches
{
    matches(int i) : _i(i) {};

    bool operator()(const A& a) const {
        return a.getVal() == _i;
    }

    bool operator()(const B& b) const {
        return b.getNum() == _i;
    }

    int _i;
};

// convenience function to express the logic in a generic way
template<class Range>
bool contains(const Range& r, int key)
{
    auto i = std::find_if(std::begin(r),
                          std::end(r),
                          matches(key));
    return i != std::end(r);
}

// test
int main()
{
    using namespace std;

    A arrayA[] = { A(1), A(2), A(3) };
    B arrayB[] = { B(3), B(4), B(5) };

    int key = 3;

    if (contains(arrayA, key) && contains(arrayB, key))
    {
        cout << "Key " << key << " is in both vectors." << endl;
    }
    else {
        cout << "Key " << key << " is not in both vectors." << endl;
    }

    return 0;
}

Upvotes: 0

Vishal
Vishal

Reputation: 158

You have inconsistent interface between class A and class B. First make the interface common by introducing a new class class C that inherits from class B. Then create a templatized function isInVec for comparision.

class A
{
    public:
        A(int iVal) : _val(iVal) {}
        int getVal() const { return _val; }
    private:
        int _val;
};

class B
{
    public:
        B(int iNum) : _num(iNum) {}
        int getNum() const { return _num; }
    private:
        int _num;
};

class C: public B
{
    public:
        C(int iNum) : B(iNum){}
        int getVal() const { return getNum(); }
};

template <typename T>
bool isInVec(std::vector<T> vect, int iVal)
{
    for(std::vector<T>::const_iterator it=vect.begin(); it != vect.end(); it++)
    {
        if(it->getVal() == iVal)
            return true;
    }
    return false;
}



int main()
{
    A arrayA[] = { A(1), A(2), A(3) };
    std::vector<A> vecA(arrayA, arrayA + sizeof(arrayA) / sizeof(A));

    C arrayC[] = { C(3), C(4), C(5) };
    std::vector<C> vecC(arrayC, arrayC + sizeof(arrayC) / sizeof(C));

    int key = 3;

    if(isInVec(vecA, key) && isInVec(vecC, key))
       std::cout << "Key " << key << " is in both vectors." << std::endl;
    else
        std::cout << "Key " << key << " is not in both vectors." << std::endl;

    return 0;


}

Upvotes: 1

MikeCAT
MikeCAT

Reputation: 75062

You can create comparator and use std::find_if.

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

class A
{
    public:
        A(int iVal) : _val(iVal) {}
        int getVal() const { return _val; }
    private:
        int _val;
};

class B
{
    public:
        B(int iNum) : _num(iNum) {}
        int getNum() const { return _num; }
    private:
        int _num;
};

class cmpA
{
    private:
        int target;
    public:
        cmpA(int t) : target(t) {}
        bool operator()(const A& a) const {
            return a.getVal() == target;
        }
};
class cmpB
{
    private:
        int target;
    public:
        cmpB(int t) : target(t) {}
        bool operator()(const B& b) const {
            return b.getNum() == target;
        }
};

template<class T, class V>
bool isInVec(const vector<V>& vector, int iNum)
{
    return find_if(vector.begin(), vector.end(), T(iNum)) != vector.end();
}

int main(void) {
    A arrayA[] = { A(1), A(2), A(3) };
    vector<A> vectorA(arrayA, arrayA + sizeof(arrayA) / sizeof(A));

    B arrayB[] = { B(3), B(4), B(5) };
    vector<B> vectorB(arrayB, arrayB + sizeof(arrayB) / sizeof(B));

    int key = 3;

    if(isInVec<cmpA>(vectorA, key) && isInVec<cmpB>(vectorB, key))
        cout << "Key " << key << " is in both vectors." << endl;
    else
        cout << "Key " << key << " is not in both vectors." << endl;

    return 0;
}

UPDATE: New code with fewer similar code, which is based on the idea of Adapter Design Pattern:

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

class A
{
    public:
        A(int iVal) : _val(iVal) {}
        int getVal() const { return _val; }
    private:
        int _val;
};

class B
{
    public:
        B(int iNum) : _num(iNum) {}
        int getNum() const { return _num; }
    private:
        int _num;
};

// dummy number getter
template<class T> int getNumber(const T& x) { return 0; }

// number getter for class A
template<> int getNumber(const A& x) { return x.getVal(); }

// number getter for class B
template<> int getNumber(const B& x) { return x.getNum(); }

// comparator using the number getter
template<class T>
class cmp
{
    private:
        int target;
    public:
        cmp(int t) : target(t) {}
        bool operator()(const T& a) const { return getNumber<T>(a) == target; }
};

template<class T>
bool isInVec(const vector<T>& vector, int iNum)
{
    return find_if(vector.begin(), vector.end(), cmp<T>(iNum)) != vector.end();
}

int main(void) {
    A arrayA[] = { A(1), A(2), A(3) };
    vector<A> vectorA(arrayA, arrayA + sizeof(arrayA) / sizeof(A));

    B arrayB[] = { B(3), B(4), B(5) };
    vector<B> vectorB(arrayB, arrayB + sizeof(arrayB) / sizeof(B));

    int key = 3;

    if(isInVec(vectorA, key) && isInVec(vectorB, key))
        cout << "Key " << key << " is in both vectors." << endl;
    else
        cout << "Key " << key << " is not in both vectors." << endl;

    return 0;
}

Upvotes: 1

Fearnbuster
Fearnbuster

Reputation: 896

I wish I could reply to your question as a comment, but I do not have enough reputation to do so.

IF the vector is sorted then using "std::binary_search" is probably the fastest algorithm that you can use for searching a vector, but remember, the vector must be sorted.

You have to include the "algorithm" file in order to use the "binary_search" algorithm (which I see you have already done).

"binary_search" may or may not work for you, because you told us to assume that you cannot change either of the classes; if that is the case, you will not be able to implement the proper overloading of the "less than" operator if such action is needed.

Check out this link for a full description of the algorithm: http://www.cplusplus.com/reference/algorithm/binary_search/

Upvotes: 0

Related Questions