[Preface: The associative C++ containers like std::map
are a bit like micro-databases with just one key column. Boost's bimap
elevates this to a two-column table with lookup in both columns, but that that's as far as the analogy goes -- there's no "polymap" that generalizes the idea.]
In any event, I want to keep thinking of maps as databases, and I now wonder if there is an iterator (or some other solution) that allows me to do a UNION of several constituent maps. That is, all maps have the same type (or value type and comparator, at least), and I want a single iterator that treats the entire collection as a big multimap (repeated keys are OK) and lets me traverse it in the correct unioned order.
Does such a thing exist, perhaps within Boost? Or is it easy to rig one up? In pseudo code:
std::map<K, M> m1, m2;
union_iterator<K, M> u(m1, m2)
for(auto it = u.begin(); it != u.end(); ++it) { /* ... */ }
For example, if we had:
m1 = { { 9:00, "Check in"}, { 12:00, "Break" }, { 16:00, "Check out"} };
m2 = { { 10:30, "coffee" }, { 12:15, "baked beans" }, { 15:00, "lies" } };
then I want the iterator to produce:
9:00, "Check in"; 10:30, "coffee"; 12:00, "Break"; 12:15, "baked beans"; ...
As I announced, I have got something pretty cool.
I'm posting it now, because I wouldn't be sure whether I'd be back in time tonight to post it. I will be spending a few words in explanation. (in this post)
A lot can be said about this code: it is not very efficient, and not very clean (yet). It is, however, nearly infinitely generic and should scale like anything else. All code can be found in a github gist:
(I'm not saying that it would be a good idea to have maps keyed with ints and floats (let alone both at the same time) - just showing that it can be done)
Here is the output of the test.cpp as you can find it:
This isn't an iterator like you asked for, but I just found this function in the standard library:
There's also a
std::set_intersection
,std::set_difference
, andstd::set_symmetric_difference
Either copying both
mapS
into a temporary, appending one to the other (in case you can modify them) or using avector
as a temporary withstd::set_union
and a custom comparator are the easiest alternative solutions.Here's how I would implement thiton's answer:
It has slightly different usage than
union_iterator<K, V>
but it implements the basic idea. You can expand the constructor to accept multiple maps however you fit, and use it in awhile (iterator.next())
loop instead of afor (...)
loop.EDIT: I simplified
next()
by doing all the popping and pushing at once. So now it's even simpler! (One could also expend some effort making it like a STL iterator, but that gets tedious.)Rigging up should be fairly easy: For N base maps, your iterator contains a priority queue prioritized by the N keys of the elements the base iterators point to. For dereference, dereference the iterator at the queue front. For increment, increment the iterator at the queue front and, if it's increment is not at the end, re-insert it.
There is a "polymap": Boost.MultiIndex.