1

I am trying to use boost::bimap for one of my requirements. Below is sample code

typedef bimap<
        multiset_of< string >,
        multiset_of< string >,
        set_of_relation<>
        > bm_type;

 bm_type bm;

 assign::insert( bm )

 ( "John" , string("lazarus" ) )
 ( "Peter", string("vinicius") )
 ( "Peter", string("test") )
 ( "Simon", string("vinicius") )
 ( "John", string("viniciusa") )
 ( "John", string("vinicius") )

I would like to do something as finding matching values for John & Peter, in other words intersection between values for John & Peter for ex: In this case it will be ("vinicius"). Can someone provide some limelight over it?

Blackhole
  • 273
  • 2
  • 12

1 Answers1

2

Here's what I came up with initially:

template <typename Value = std::string, typename Bimap, typename Key>
std::set<Value> projection(Bimap const& bm, Key const& key)
{
    std::set<Value> p;
    auto range  = bm.left.equal_range(key);
    auto values = boost::make_iterator_range(range.first, range.second);

    for (auto& relation : values)
        p.insert(relation.template get<boost::bimaps::member_at::right>());

    return p;
}

auto john  = projection(bm, "John");
auto peter = projection(bm, "Peter");

std::multiset<std::string> intersection;
std::set_intersection(
         john.begin(), john.end(),
         peter.begin(), peter.end(),
         inserter(intersection, intersection.end())
     );

I think it can be more efficient. So I tried replacing the projection on the fly using Boost Range's adaptors:

struct GetRightMember
{
    template <typename> struct result { typedef std::string type; };

    template <typename T>
    std::string operator()(T const& v) const {
        return v.template get<boost::bimaps::member_at::right>();
    }
};

const GetRightMember getright;
std::cout << "Intersection: ";

// WARNING: broken: ranges not sorted
boost::set_intersection(
        bm.left.equal_range("John")  | transformed(getright),
        bm.left.equal_range("Peter") | transformed(getright),
        std::ostream_iterator<std::string>(std::cout, " "));

Sadly it doesn't work - presumably because the transformed ranges aren't sorted.

So I'd stick with the more verbose version (or reconsider my data structure choices). See it Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • and same would even work if I use bimap of unordered_multiset_of ? – Blackhole Mar 06 '14 at 15:14
  • I strongly expect this. Have you tried? **Update** 59 seconds later: http://coliru.stacked-crooked.com/a/69e331d13cef1c65 – sehe Mar 06 '14 at 15:34
  • I can implement my use case by having unordered_map (key 2 set of values) & another unordered_map < string, unordered_set > (value to 2 unordered_set of keys). In that case I would need to manage remove from first map if something got removed in second and viceversa (& of course same for add as well) but that way I won't need to create sets for finding intersection between different keys, do you think this would be more efficient than using bimap? – Blackhole Mar 06 '14 at 15:45
  • I made use of vector, may be this would be more efficient. Any suggestions? http://coliru.stacked-crooked.com/a/11c7df52bfbae15f – Blackhole Mar 06 '14 at 15:54
  • @Peetish I'd expect the vector version to be faster, at least for smaller right-hand subsets. The proof of course is in the profiling. I trust you can do this. – sehe Mar 06 '14 at 16:56
  • What about my other suggestion about keeping 2 separate unordered_map as I explained? Do you have any suggestions for that vs bimap ? – Blackhole Mar 06 '14 at 17:52
  • You can. It seems to me you've described it so I don't know what I should suggest. Obviously the performance comparison is going to depend exclusively on the use patterns. – sehe Mar 06 '14 at 22:16