unable to apply std::set_intersection on different types of structs with a common field

前端 未结 3 538
南方客
南方客 2021-01-12 14:57

I am trying to use use std::set_intersection to find common elements between 2 completely different types of data structures that have a common binding \'na

相关标签:
3条回答
  • 2021-01-12 15:12

    use std::copy_if and lambda that searches inside vectorB using std::binary_search be sure to sort using the same predicate you will give to binary_search

    this solves similar problem: elegant way to remove all elements of a vector that are contained in another vector?

    0 讨论(0)
  • 2021-01-12 15:20

    Thanks to the expressiveness of C++, there are a few ways you can solve this problem. The following is by no means an exhaustive list.

    1. Implicitly convert both types to a wrapper struct for comparison

    If you're attached to using lambdas, define a type that can be implicitly constructed from both StructA and StructB and wraps the fields used for comparison. This can allow for additional logic to be performed to the fields in the constructor before comparison. For example:

    struct Common {
        std::string const& mCommonField;
        Common(StructA const& sa) : mCommonField{sa.mCommonField} {};
        Common(StructB const& sb) : mCommonField{sb.mCommonField} {};
    };
    

    Then your comparison lambda can be written

    auto cmp = [](Common const& lhs, Common const& rhs) {
        return lhs.mCommonField < rhs.mCommonField;
    };
    

    and used like

    std::sort(aStructs.begin(), aStructs.end(), cmp);
    std::sort(bStructs.begin(), bStructs.end(), cmp);
    // ...
    std::set_intersection(aStructs.begin(), aStructs.end(),
                          bStructs.begin(), bStructs.end(),
                          std::back_inserter(intersection),
                          cmp
                          );
    

    Live example on Coliru Viewer.

    2. Use a comparator with templated operator().

    Instead of using a lambda, define a functor with a templated operator().

    struct comparator
    {
        template<typename T, typename U>
        bool operator()(T const& lhs, U const& rhs) const {
            return lhs.mCommonField < rhs.mCommonField;
        }
    };
    

    Then, it's as easy as:

    std::sort(aStructs.begin(), aStructs.end(), comparator{});
    std::sort(bStructs.begin(), bStructs.end(), comparator{});
    // ...
    std::set_intersection(aStructs.begin(), aStructs.end(),
                          bStructs.begin(), bStructs.end(),
                          std::back_inserter(intersection),
                          comparator{}
                          );
    

    Just note that as there is a template in the comparator, it must be declared outside of function scope. Live example on Coliru Viewer.

    3. Wait for C++14

    And with generic lambdas added to C++14, you can use the following with a conformant compiler:

    auto cmp = [](auto lhs, auto rhs) { return lhs.mCommonField < rhs.mCommonField; };
    // ...
    std::sort(aStructs.begin(), aStructs.end(), cmp);
    std::sort(bStructs.begin(), bStructs.end(), cmp);
    // ...
    std::set_intersection(aStructs.begin(), aStructs.end(),
                          bStructs.begin(), bStructs.end(),
                          std::back_inserter(intersection),
                          cmp);
    

    Again, live example on Coliru Viewer.


    Also, C-style struct typedefs are unnecessary in C++ (and arguably unclear most places in C), so anywhere you have

    typedef struct Foo {
        // ...
    } Foo;
    

    you can replace it with

    struct Foo {
        // ...
    };
    

    without any other changes required of your code.

    0 讨论(0)
  • 2021-01-12 15:26

    set_intersection isn't magic, if you have sorted vectors it is very easy to roll your own, roughly like this:

    auto ta = aStructs.begin();
    auto tb = bStructs.begin();
    while( ta != aStructs.end() && tb != bStructs.end() ){
        if( ta->mCommonField < tb->mCommonField ){
            ++ta;
        } else if( tb->mCommonField < ta->mCommonField ){
            ++tb;
        } else {
            std::cout << "found common: " << ta->mCommonField << std::endl;
            ++ta;
            ++tb;
        }
    }
    
    0 讨论(0)
提交回复
热议问题