Json string Comparision - c++

I am working on a c++ project where I need to compare two or more Json string that will be passed to me as arguments in a function and i have to return a bool accordingly. I am using Jsoncpp but I am unable to compare the entirety of the two Json datas. I want to know the best procedure to loop in the key and value and check the value with corresponding value of another json string (both String will be passed to the function and will be parsed using reader.parse() of jsoncpp and then i need to compare them both and return the bool value). Can anyone help me with this please? thank you in advance.
The place where I am stuck:
class test {
public:
static bool isequalstring(const std::string &item1, const std::string
&item2, const std::string &temp) {
Document d1;
d1.Parse(item1.c_str());
Document d2;
d2.Parse(item2.c_str());
Document d3;
d3.Parse(temp.c_str());
bool matched = true;
//itr= iterate through the third json to get the keys and match the keys in first and second
for (auto itr = d3.MemberBegin(); itr != d3.MemberEnd(); itr++) {
if (d1.HasMember(itr->name) && d2.HasMember(itr->name)) { // if the member doesn't exist in both, break
if (d1[itr->name] != d2[itr->name]) {
// value doesn't match, then break
matched = false;
break;
}
} else {
matched = false;
break;
}
}
return matched;
}
};
bool testDeepNestedJson_should_succeed(){
bool expectedTestResult = true;
bool testResult;
// Input 1 JSON Object
const char* input1 = "{\"array\":[1,2,3],\"boolean\":true,\"null\":null,\"number\":123,\"object\":{\"a\":\"b\",\"c\":\"d\",\"e\":\"f\"},\"string\":\"Hello World\",\"object_array\":[{\"key\":\"value1\"},{\"key\":\"value2\"},{\"key\":\"value3\"}],\"deep_nested_array\":[{\"object_array\":[{\"key\":\"value1\"},{\"key\":\"value2\"},{\"key\":\"value3\"}]},{\"object_array\":[{\"key\":\"value4\"},{\"key\":\"value5\"},{\"key\":\"value6\"}]}]}";
const char* input2 = "{\"array\":[1,2,3],\"justsomedata\":true,\"boolean\":true,\"null\":null,\"object\":{\"a\":\"b\",\"c\":\"d\",\"e\":\"f\"},\"number\":123,\"object_array\":[{\"key\":\"value1\"},{\"key\":\"value2\"},{\"key\":\"value3\"}],\"deep_nested_array\":[{\"object_array\":[{\"key\":\"value1\"},{\"key\":\"value2\"},{\"key\":\"value3\"}]},{\"object_array\":[{\"key\":\"value4\"},{\"key\":\"value5\"},{\"key\":\"value6\",\"ignoreme\":12346}]}],\"string\":\"Hello World\"}";
const char* stencil = "{\"array\":[null],\"boolean\":null,\"null\":null,\"object\":{\"a\":null,\"c\":null,\"e\":null},\"number\":null,\"object_array\":[{\"key\":null}],\"deep_nested_array\":[{\"object_array\":[{\"key\":null}]}],\"string\":null}";
testResult = test::isequalstring(input1, input2, stencil);
if(testResult != expectedTestResult){
std::cout<<"testDeepNestedJson_should_succeed:"<<std::endl;
std::cout<<"Item1:"<<input1<<std::endl;
std::cout<<"Item2:"<<input2<<std::endl;
std::cout<<"Stencil:"<<stencil<<std::endl;
std::cout<<"Test Failed result is: False expected was: True"<<std::endl;
return false;
}
std::cout<<"PASSED: testDeepNestedJson_should_succeed"<<std::endl;
return true;
}
int main() {
testDeepNestedJson_should_succeed();
return 0;
}

Using RapidJSON, the code would be something like this
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
//({ id: 1, name : "test", randomNo : 1 }, { id: 1, name : "test", randomNo : 1 }, { id: null, name : null, randomNo : null }) //shoult assert true
//isEqualItem({id: 1, name: "test", randomNo: 1}, {id: 1, name: "test", randomNo: 2}, {id: null, name: null, randomNo: null}) //shoult assert false
//isEqualItem({id: 1, name: "test", randomNo: 1}, {id: 1, name: "test", randomNo: 3}, {id: null, name: null}) //shoult assert true
bool is_same(const std::string& s1, const std::string& s2, const std::string& s3) {
Document d1;
d1.Parse(s1.c_str());
Document d2;
d2.Parse(s2.c_str());
Document d3;
d3.Parse(s3.c_str());
bool matched = true;
// iterate through the third json to get the keys and match the keys in first and second
for (Value::ConstMemberIterator itr = d3.MemberBegin(); itr != d3.MemberEnd(); itr++) {
if (d1.HasMember(itr->name) && d2.HasMember(itr->name)) { // if the member doesn't exist in both, break
if (d1[itr->name] != d2[itr -> name]) { // value doesn't match, then break
matched = false;
break;
}
}
else {
matched = false;
break;
}
}
return matched;
}
int main() {
// 1. Parse a JSON string into DOM.
const char* json = "{\"id\":1,\"name\":\"test\",\"randomNo\":1}";
const char* json2 = "{\"id\":1,\"name\":\"test\",\"randomNo\":2}";
const char* keys = "{\"id\":\"null\",\"name\":\"null\"}";
if (is_same(json, json2,keys)) {
std::cout << "Both are same" << std::endl;
}
return 0;
}

You could iterate over root2, get name of keys using name(), access values with that names in root and root1 using operator[], and compare them using operator==:
for (auto it = root2.begin(); it != root2.end(); ++it) {
auto name = it.name();
if (root[name] != root1[name])
return false;
}
return true;
BTW. You parse item1 to root, item2 to root1 and temp to root2. You could be more consistent in naming things.

Related

Find number of occurrences in a map C++

I am trying to create a map in C++ and I will use it to compare wheater a vector of string match another one. The condition is that only one word can be taken into consideration. For example, given:
{"two", "times", "three", "is", "not", "four"}
{"two", "times", "two", "is", "four"}
In this case they shouldn't match because there is only one "two" in the first vector.
My code is as follows:
#include <iostream>
#include <vector>
#include <map>
using namespace std;
void checkMagazine(vector<string> magazineWords, vector<string> noteWords)
{
map<string, int> magazine;
map<string, int>::iterator it = magazine.begin();
//populating the map
for (string magazineWordString : magazineWords)
{
it = magazine.find(magazineWordString);
if (it != magazine.end())
{
int numberOfOccurences = it->second;
magazine.insert(pair<string, int>(magazineWordString, numberOfOccurences + 1));
}
else
{
magazine.insert(pair<string, int>(magazineWordString, 1));
}
}
//checking for correspondences
for (string noteWordString : noteWords)
{
it = magazine.find(noteWordString);
if (it != magazine.end())
{
int numOfOccurences = it->second;
magazine.insert(pair<string, int>(noteWordString, numOfOccurences - 1));
}
else
{
cout << "There is no match." << endl;
return;
}
}
cout << "There is a match!" << endl;
}
There's a far easier way! The subscript operator on a map will try to find the key in the map. If it is successful, it returns a reference to its value. Otherwise, it creates a new key/value pair for you on the spot and gives you a reference to its value.
So since the default value of an int will be 0 (that is, when we create a new key/value pair, the value will be 0), all your work can actually just be:
bool checkMagazine(vector<string> magazineWords, vector<string> noteWords)
{
map<string, int> bank;
//populating the map
for (string magazineWord : magazineWords) {
++bank[magazineWord];
}
//checking for correspondences
for (string noteWord : noteWords) {
if(--bank[noteWord] < 0) { return false; }
}
return true;
}
Watch it run here: https://ideone.com/MzLAJM

cpp RapidJSON - Resolve key conflicts without information loss

I want to parse a text file which is similar to JSON. After some character conversions, it still has some objects, which have key conflicts. So my JSON looked like this:
{
"key1": {
"a": "asdf",
"a": "foo",
"a": "bar",
"a": "fdas"
}
}
And i wanted to resolve it into this:
{
"key1": {
"a": [
"asdf",
"foo",
"bar",
"fdas"
]
}
}
I tried to achieve this with JsonCpp, but it can't handle the key conflicts. So i chose to use RapidJSON, especially because it CAN keep all the key-conflict-members when parsing.
To then resolve the key conflicts without loosing information, i wrote the following recursive RapidJSON cpp code:
void resolveKeyConflicts(rj::Value& value) {
if (value.IsObject()) {
std::map<std::string, unsigned int> nameCount;
for (rj::Value::MemberIterator vMIt = value.MemberBegin();
vMIt != value.MemberEnd(); vMIt++) {
std::string name(vMIt->name.GetString());
if (nameCount.find(name) == nameCount.end()) {
nameCount[name] = 1;
} else {
nameCount[name] += 1;
}
}
for (std::map<std::string, unsigned int>::iterator nCIt =
nameCount.begin(); nCIt != nameCount.end(); nCIt++) {
if (nCIt->second > 1) {
rj::Value newArray(rj::kArrayType);
for (rj::Value::MemberIterator vFMIt = value.FindMember(
nCIt->first.c_str()); vFMIt != value.MemberEnd();
vFMIt++) {
if (vFMIt->name.GetString() == nCIt->first) {
rj::Value value(vFMIt->value, this->GetAllocator());
newArray.PushBack(value, this->GetAllocator());
}
}
value.EraseMember(value.FindMember(nCIt->first.c_str()),
value.MemberEnd());
rj::Value key(nCIt->first.c_str(), nCIt->first.length(),
this->GetAllocator());
value.AddMember(key, newArray, this->GetAllocator());
}
}
for (rj::Value::MemberIterator vMIt = value.MemberBegin();
vMIt != value.MemberEnd(); vMIt++) {
if (vMIt->value.IsObject() || vMIt->value.IsArray()) {
resolveKeyConflicts(vMIt->value);
}
}
} else if (value.IsArray()) {
for (rj::Value::ValueIterator vVIt = value.Begin(); vVIt != value.End();
vVIt++) {
resolveKeyConflicts(*vVIt);
}
}
}
This works pretty good as long as the conflicting key-members are the only members in that object. This can, i think, be archived with simpler code, but i additionally tried to be able to resolve arbitrary key conflicts like this:
{
"key2": {
"a": "asdf",
"b": "foo",
"b": "bar",
"c": "fdas"
}
}
Into this:
{
"key2": {
"a": "asdf",
"b": [
"foo",
"bar"
],
"c": "fdas"
}
}
Turns out FindMember does not, as i thought, gives back an iterator over all members with the same key name, but just the position of the first member with that key. I think my python way of thinking may have messed with my expectations on FindMember. So like this, the code is going to lose the "c": "fdas" member.
I relied on MemberIterator EraseMember(MemberIterator first, MemberIterator last) because all of the other methods to remove a member mentioned in http://rapidjson.org/md_doc_tutorial.html#ModifyObject seem to have problems removing the last member in the key1 case. But EraseMember like this is definitely the wrong choice for the key2 case.
So I'm kind of lost here. Can please somebody point me into the right direction to resolve the key conflicts without information loss, which can handle both the key1 and the key2 case?
edit: I'm using RapidJSON from https://github.com/miloyip/rapidjson/tree/v1.0.2 which is at the v1.0.2 tag.
I think the tricky part is to memorize whether the key has already expanded to an array (because the value may be originally an array).
So, another way is firstly convert all key: value into key:[value], do the merge, and then convert back to key: value if there is only one element in the array.
This is my attempt:
static void MergeDuplicateKey(Value& v, Value::AllocatorType& a) {
if (v.IsObject()) {
// Convert all key:value into key:[value]
for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
itr->value = Value(kArrayType).Move().PushBack(itr->value, a);
// Merge arrays if key is duplicated
for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd();) {
Value::MemberIterator itr2 = v.FindMember(itr->name);
if (itr != itr2) {
itr2->value.PushBack(itr->value[0], a);
itr = v.EraseMember(itr);
}
else
++itr;
}
// Convert key:[values] back to key:value if there is only one value
for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) {
if (itr->value.Size() == 1)
itr->value = itr->value[0];
MergeDuplicateKey(itr->value, a); // Recursion on the value
}
}
else if (v.IsArray())
for (Value::ValueIterator itr = v.Begin(); itr != v.End(); ++itr)
MergeDuplicateKey(*itr, a);
}
I tested it in this commit.
I completely rewrote that part, trying (again) another approach. I think i found a pretty elegant solution:
void resolveKeyConflicts(rj::Value& value) {
if (value.IsObject()) {
std::vector<std::string> resolvedConflicts;
rj::Value newValue(rj::kObjectType);
for (rj::Value::MemberIterator vMIt = value.MemberBegin();
vMIt != value.MemberEnd(); vMIt++) {
rj::Value::MemberIterator nVFMIt = newValue.FindMember(vMIt->name);
if (nVFMIt == newValue.MemberEnd()) {
rj::Value newKey(vMIt->name, this->GetAllocator());
newValue.AddMember(newKey, vMIt->value, this->GetAllocator());
} else {
std::string conflict(vMIt->name.GetString(),
vMIt->name.GetStringLength());
if (std::find(resolvedConflicts.begin(),
resolvedConflicts.end(), conflict)
== resolvedConflicts.end()) {
rj::Value newArray(rj::kArrayType);
nVFMIt->value.Swap(newArray);
nVFMIt->value.PushBack(newArray, this->GetAllocator());
nVFMIt->value.PushBack(vMIt->value, this->GetAllocator());
resolvedConflicts.push_back(conflict);
} else {
nVFMIt->value.PushBack(vMIt->value, this->GetAllocator());
}
}
}
value.SetNull().SetObject();
for (rj::Value::MemberIterator nVMIt = newValue.MemberBegin();
nVMIt != newValue.MemberEnd(); nVMIt++) {
if (nVMIt->value.IsObject() || nVMIt->value.IsArray()) {
this->resolveKeyConflicts(nVMIt->value);
}
value.AddMember(nVMIt->name, nVMIt->value, this->GetAllocator());
}
} else if (value.IsArray()) {
for (rj::Value::ValueIterator vVIt = value.Begin(); vVIt != value.End();
vVIt++) {
if (vVIt->IsObject() || vVIt->IsArray()) {
this->resolveKeyConflicts(*vVIt);
}
}
}
}
I'm not so sure about the value.SetNull().SetObject() part for emptying value, but it works.
If you think there is room for improvement, just let me know where. Thanks.

Parsing object inside array in rapidjson

I'm having problems implementing a recursive function that goes over the tree I get from the parsing of a json input.
json input. e.g.:
{
"attr" : { "a": 1, "ovec": [ { "b": 2, "c": 3 }, { "d": 4} ] }
}
This is what we call a 'compound value of an attribute', and the value is simply a JSON doc. Its content is completely arbitrary (as long as its valid JSON).
The problem is that with a Vector I have to loop using the type Value::ConstValueIterator (unlike for Object, where I use Value::ConstMemberIterator).
My recursive function has Value::ConstMemberIterator as parameter and all is OK until I encounter a Vector/Object inside a Vector - for the recursive call I'd need an iterator of the type Value::ConstMemberIterator.
Relevant parts of the "traversing" function:
int parseContextAttributeCompoundValue
(
const Value::ConstMemberIterator& node
)
{
std::string type = jsonParseTypeNames[node->value.GetType()];
if (type == "Array")
{
for (Value::ConstValueIterator iter = node->value.Begin(); iter != node->value.End(); ++iter)
{
std::string nodeType = jsonParseTypeNames[iter->value.GetType()];
if (nodeType == "String")
{
val = iter->GetString();
}
// else if ...
if ((nodeType == "Object") || (nodeType == "Array"))
{
// Here's my problem - need to convert 'iter' to Value::ConstMemberIterator
// in order to recursively call parseContextAttributeCompoundValue for this object/array
parseContextAttributeCompoundValue(iter); // COMPILATION ERROR
}
}
}
else if (type == "Object")
{
for (Value::ConstMemberIterator iter = node->value.MemberBegin(); iter != node->value.MemberEnd(); ++iter)
{
std::string nodeType = jsonParseTypeNames[iter->value.GetType()];
if (nodeType == "String")
{
val = iter->value.GetString();
}
else if (nodeType == "Number")
{
if ((nodeType == "Object") || (nodeType == "Array"))
{
// Here I'm just fine as iter is of the desired type already
parseContextAttributeCompoundValue(iter);
}
}
}
}
I've tried a few things like calling iter->value.MemberBegin() to "convert" to the desired type, but so far without any success
More than thankful for some help here ...
You can simply call a function with a Value type, instead of passing iterator:
void parseContextAttributeCompoundValue(const Value& v) {
if (v.IsObject()) {
// ...
}
else if (v.IsArray() {
// ...
}
}
And then from the calling site:
for (Value::ConstValueIterator iter = ...) {
parseContextAttributeCompoundValue(*iter);
}
for (Value::ConstMemberIterator iter = ...) {
parseContextAttributeCompoundValue(iter->value);
}

rewrite access to collection to avoid "double" finding

I have such code:
std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;
auto i = futOrders.find(orderId);
if (i == futOrders.end()) {
LimitOrder& newOrder = futOrders[orderId];
// work
} else {
LimitOrder& futOrder = i->second;
// another work
}
Here I execute "find" twice:
first time: auto i = futOrders.find(orderId);
second time: LimitOrder& newOrder = futOrders[orderId];
Can i rewrite it somehow to avoid "double find"?
You can perform an emplace, and check the return value to know whether the item was inserted or not:
std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;
auto i = futOrders.emplace(
std::piecewise_construct, std::tie(orderId), std::make_tuple());
if (i.second) {
LimitOrder& newOrder = i.first->second;
// work
} else {
LimitOrder& futOrder = i.first->second;
// another work
}
How about using size() to realize if an element was inserted, like this:
auto old_size = futOrders.size();
LimitOrder& order = futOrders[orderId];
if (old_size < futOrders.size()) {
LimitOrder& newOrder = order;
// work
} else {
LimitOrder& futOrder = order;
// another work
}
Assuming there is a way to "determine if an order is empty", you could do:
LimitOrder& anOrder = futOrders[orderId];
if (anOrder.empty())
{
// New order, do stuff that only new orders need.
}
else
{
// Old order, update it.
}
The empty method could of course be something like if (anOrder.name == "") or if (anOrder.orderId == 0), etc.
You can use this overload of insert instead:
std::pair<iterator,bool> insert( const value_type& value );
Example:
std::unordered_map<int, std::string> m { {0, "A"}, {1, "B"}, {2, "C"} };
int orderId = 1;
// attempt to insert with key you have and default constructed value type
auto p = m.insert( std::make_pair(orderId, std::string()) );
if (p.second) {
// the element was inserted
} else {
// the element was not inserted
std::cout << p.first->second; // will print "B"
}
In both cases, p.first is the iterator to the element you search for (or just got inserted).

Union iterator for maps?

[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"; ...
There is a "polymap": Boost.MultiIndex.
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)
PS. The includes will be trimmed down (to about 20%); I will probably do some more general work on the code too.
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:
merge_maps_iterator.hpp
Makefile
test.cpp - a rather arcane set of test-cases showing off the genericity (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:
== input ========================================
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
{ b, 3.14 }
== output =======================================
2: aap;
23: mies;
98: 3.14;
100: noot;
101: broer;
== input ========================================
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
2: aap;
23: mies;
98: 3.14;
100: noot;
101: broer;
== input ========================================
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
2: aap;aap;
23: mies;mies;
100: noot;noot;
101: broer;broer;
== input ========================================
{ b, 3.14 }
{ b, 3.14 }
== output =======================================
b: 3.14;3.14;
== input ========================================
{ 1.0, dag } { 22.0, bye } { 24.0, Tschüß }
{ 1, true } { 22, false } { 24, true }
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
1.0: dag;true;
2.0: aap;
22.0: bye;false;
23.0: mies;
24.0: Tschüß;true;
98.0: 3.14;
100.0: noot;
101.0: broer;
== input ========================================
{ 1.0, dag } { 2.0, EXTRA } { 22.0, bye } { 24.0, Tschüß }
{ 1, true } { 22, false } { 24, true }
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
1.0: dag;true;
2.0: EXTRA;aap;
22.0: bye;false;
23.0: mies;
24.0: Tschüß;true;
98.0: 3.14;
100.0: noot;
101.0: broer;
Either copying both mapS into a temporary, appending one to the other (in case you can modify them) or using a vector as a temporary with std::set_union and a custom comparator are the easiest alternative solutions.
Here's how I would implement thiton's answer:
template <class container> class union_iterator
{
private:
typedef std::pair<typename container::const_iterator, typename container::const_iterator> container_range;
class container_range_compare
{
public:
bool operator()(const container_range &lhs, const container_range &rhs) const
{
return typename container::value_compare()(*lhs.first, *rhs.first);
}
};
std::priority_queue<container_range, container_range_compare> m_range_queue;
container::const_iterator m_current_iterator;
bool m_is_valid;
void add_container(const container &cont)
{
add_container_range(std::make_pair(cont.begin(), cont.end()));
}
void add_container_range(const container_range &range)
{
if (range.first!=range.second)
{
m_range_queue.push(range);
}
}
public:
union_iterator(const container &a): m_valid(false)
{
add_container(a);
}
bool next()
{
m_is_valid= false;
if (!m_range_queue.empty())
{
container_range range= m_range_queue.pop();
m_current_iterator= range.first;
++range.first;
add_container_range(range);
m_is_valid= true;
}
return m_is_valid;
}
typename const container::value_type &operator *() const
{
return *m_current_iterator;
}
typename const container::value_type *operator ->() const
{
return m_current_iterator.operator ->();
}
};
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 a while (iterator.next()) loop instead of a for (...) 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.)
Very simple solution using boost function_output_iterator:
typedef std::map< std::string, std::string > Map;
Map first_map, second_map;
... // fill maps
// iterate over maps union
std::merge(
first_map.begin(), first_map.end(),
second_map.begin(), second_map.end(),
boost::make_function_output_iterator(
[]( const Map::value_type & pair )
{
std::cout <<
"key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
),
first_map.value_comp()
);
We can make this solution prettier by using boost::set_union (range version) instead of std::set_union.
UPD Updated version use different key/values types:
typedef std::map< int, char > FirstMap;
typedef std::map< short, std::string > SecondMap;
FirstMap first_map;
SecondMap second_map;
... // fill maps
struct CustomOutput
{
void operator()( const FirstMap::value_type & pair ) const
{
std::cout << "key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
void operator()( const SecondMap::value_type & pair ) const
{
std::cout << "key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
};
struct CustomPred
{
bool operator()( const FirstMap::value_type & first_pair, const SecondMap::value_type & second_pair ) const
{ return first_pair.first < second_pair.first; }
bool operator()( const SecondMap::value_type & second_pair, const FirstMap::value_type & first_pair ) const
{ return second_pair.first < first_pair.first; }
};
// iterate over maps union
std::merge(
first_map.begin(), first_map.end(),
second_map.begin(), second_map.end(),
boost::make_function_output_iterator( CustomOutput() ),
CustomPred()
);
UPD2 std::set_union replaced with std::merge
Or is it easy to rig one up?
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.
Here's how it can be done quite easily:
template<class It>
class union_iterator
{
public:
union_iterator(It it1_begin, It it1_end, It it2_begin, It it2_end)
: current1(it1_begin), current2(it2_begin), end1(it1_end), end2(it2_end)
{ if (it1_begin != it1_end && it2_begin != it2_end) {
if (*it1_begin < *it2_begin) { current= &current1; }
else { current = &current2; }
} else if (it1_begin==it1_end) { current=&current2; }
else { current = &current1; }
}
void operator++() {
if (current1!=end1 && current2 !=end2) {
if (*current1 < *current2)
{ ++current1; current = &current1; }
else { ++current2; current=&current2; }
} else if (current1==end1 && current2 != end2) {
++current2;
current = &current2;
} else if (current1!=end1 && current2 == end2) {
++current1;
current = &current1;
}
}
typename std::iterator<It1>::value_type operator*() { return **current; }
private:
It current1;
It current2;
It end1;
It end2;
It *current;
};
But the real problem is implementing all the remaining member functions required by normal iterators :-). Boost has some lib for helping you do it, but it might still be quite difficult.
This isn't an iterator like you asked for, but I just found this function in the standard library:
§ 25.4.5.2 set_union [set.union]
template<class InputIterator1, class InputIterator2,
class OutputIterator, class Compare>
OutputIterator
set_union(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
Effects: Constructs a sorted intersection of the elements from the two ranges; that is, the set of elements that are present in both of the ranges.
Requires: The resulting range shall not overlap with either of the original ranges.
Returns: The end of the constructed range.
Complexity: At most 2 * ((last1 - first1) + (last2 - first2)) - 1 comparisons.
Remarks: If [first1,last1) contains m elements that are equivalent to each other and [first2, last2) contains n elements that are equivalent to them, the first min(m, n) elements shall be copied from the first range to the output range, in order.
There's also a std::set_intersection, std::set_difference, and std::set_symmetric_difference