views:

119

answers:

2

Not used set_intersection before, but I believe it will work with maps. I wrote the following example code but it doesn't give me what I'd expect:

#include <map>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

struct Money
{
    double amount;
    string currency;

    bool operator< ( const Money& rhs ) const
    {
        if ( amount != rhs.amount )
            return ( amount < rhs.amount );
        return ( currency < rhs.currency );
    }
};

int main( int argc, char* argv[] )
{
    Money mn[] =
    {
        { 2.32,  "USD" },
        { 2.76,  "USD" },
        { 4.30,  "GBP" },
        { 1.21,  "GBP" },

        { 1.37,  "GBP" },
        { 6.74,  "GBP" },
        { 2.55,  "EUR" }
    };

    typedef pair< int, Money > MoneyPair;
    typedef map< int, Money > MoneyMap;

    MoneyMap map1;
    map1.insert( MoneyPair( 1, mn[0] ) );
    map1.insert( MoneyPair( 2, mn[1] ) );
    map1.insert( MoneyPair( 3, mn[2] ) );  // (3)
    map1.insert( MoneyPair( 4, mn[3] ) );  // (4)

    MoneyMap map2;
    map2.insert( MoneyPair( 3, mn[2] ) );  // (3)
    map2.insert( MoneyPair( 4, mn[3] ) );  // (4)
    map2.insert( MoneyPair( 5, mn[4] ) );
    map2.insert( MoneyPair( 6, mn[5] ) );
    map2.insert( MoneyPair( 7, mn[6] ) );

    MoneyMap out;
    MoneyMap::iterator out_itr( out.begin() );
    set_intersection( map1.begin(), map1.end(), map2.begin(), map2.end(), inserter( out, out_itr ) );

    cout << "intersection has " << out.size() << " elements." << endl;
    return 0;
}

Since the pair labelled (3) and (4) appear in both maps, I was expecting that I'd get 2 elements in the intersection, but no, I get:

intersection has 0 elements.

I'm sure this is something to do with the comparitor on the map / pair but can't figure it out.

+5  A: 
MoneyMap map2;
map1.insert( MoneyPair( 3, mn[3] ) );  // (3)
map1.insert( MoneyPair( 4, mn[4] ) );  // (4)
map1.insert( MoneyPair( 5, mn[5] ) );
map1.insert( MoneyPair( 6, mn[6] ) );
map1.insert( MoneyPair( 7, mn[7] ) );

Unless this is a typo, you are just reinserting stuff into map1 instead of inserting into map2. I tested it out with the corrected code and it outputted "Intersection has 2 elements."

Niki Yoshiuchi
Thanks, you're right and I've corrected the code. I had retyped this from the original code at a clients site. The client has strict rules on posting code out so I had to re-enter and didn't spot the bug.
Robin Welch
+2  A: 

Niki is certainly correct about your typo -- map2 is empty here! However you need to be careful about something else.

Let's say your code looked like this:

MoneyMap map1;
map1.insert( MoneyPair( 1, mn[1] ) );
map1.insert( MoneyPair( 2, mn[2] ) );
map1.insert( MoneyPair( 3, mn[3] ) );  // (3)
map1.insert( MoneyPair( 4, mn[4] ) );  // (4)

MoneyMap map2;
map2.insert( MoneyPair( 3, mn[4] ) );  // (3)
map2.insert( MoneyPair( 4, mn[3] ) );  // (4)
map2.insert( MoneyPair( 5, mn[6] ) );
map2.insert( MoneyPair( 6, mn[5] ) );
map2.insert( MoneyPair( 7, mn[1] ) );

MoneyMap out;
MoneyMap::iterator out_itr( out.begin() );
set_intersection(map1.begin(), map1.end(), 
                 map2.begin(), map2.end(), 
                 inserter( out, out_itr ) );

Now, what would happen? You'd find that out would be empty because set_intersection uses std::less to compare elements, and the elements of your maps are pairs -- thus (3, mn[3]) differs from (3, mn[4]).

The other way you could do this is by writing

set_intersection(map1.begin(), map1.end(), 
                 map2.begin(), map2.end(), 
                 inserter( out, out_itr ), map1.value_comp() );

Now, out will contain two elements: (3, mn[3]) and (4, mn[4]), because their keys match. The elements are always copied from the first iterator range.

Note that maps are always sorted by the type map::value_compare they contain. If you're using a funky comparison function, set_intersection will not work without the comparison functor explicitly supplied if the elements of the map don't happen to be in order with respect to std::less.

rlbond
Thanks very much. The problem in my case was a bug in the operator<()
Robin Welch