I have a function that have very similar repeating code. I like to refactor it, but don't want any complex mapping code.
The code basically filter out columns in a table. I made this example simple by having the comparison statement having a simple type, but the real comparison can be more complex.
I am hoping there may be some template or lambda technique that can do this.
vector<MyRecord*>& MyDb::Find(bool* field1, std::string * field2, int* field3)
{
std::vector<MyRecord*>::iterator iter;
filterList_.clear();
std::copy(list_.begin(), list_.end(), back_inserter(filterList_));
if (field1)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field1 != *field1)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
if (field2)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field2 != *field2)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
if (field3)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field3 != *field3)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
return filterList_;
}
Update: Just in case someone is curious, this is my final code. Thanks again everyone. A lot easy to understand and maintain.
vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3)
{
auto compare = [&](MyRecord* rec) {
bool add = true;
if (field1 && rec->field1 != *field1) {
add = false;
}
if (field2 && rec->field2 != *field2) {
add = false;
}
if (field3 && rec->field3 != *field3) {
add = false;
}
return add;
};
filterList_.clear();
std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_), compare);
return filterList_;
}
you can use std::copy_if (as you already/would do a copy anyway)
vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3){
filterList_.clear();
std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_),[&](MyRecord* rec){
// whatever condition you want.
return field3 && rec->field3 != *field3;
});
return filterList_;
}
Is there a simple way of refactoring this code?
As far as I understood your algorithm/ intention, using std::erase_if (c++20) you can replace the entire while loops as follows (Demo code):
#include <vector> // std::erase_if
std::vector<MyRecord*> // return by copy as filterList_ is local to function scope
Find(bool* field1 = nullptr, std::string* field2 = nullptr, int* field3 = nullptr)
{
std::vector<MyRecord*> filterList_{ list_ }; // copy of original
const auto erased = std::erase_if(filterList_, [=](MyRecord* record) {
return record
&& ((field1 && record->field1 != *field1)
|| (field2 && record->field2 != *field2)
|| (field3 && record->field3 != *field3));
}
);
return filterList_;
}
If no support for C++20, alternatively you can use erase–remove idiom, which is in effect happening under the hood of std::erase_if.
How do I remove key from a Poco json while iterating it? Like:
Poco::JSON::Object::Ptr poco_json;
for (auto& objs : *poco_json)
{
// do something
if (objs.first == "specific key")
poco_json->remove(key);
}
or
Poco::JSON::Object::Ptr poco_json;
for(auto it = poco_json->begin();it != poco_json->end();)
{
// do something
if (it->first == "specific key")
it = poco_json->remove(it->first);//error : poco didn't have something like this
else
++it;
}
the problem is after remove a key from the json, it will invalidate the iterators. I know that in std::map, erase return the valid iterator for next iteration, but I cant find something similar for Poco json.
std::map::erase returns iterator to next item since C++11, before c++11 you erase items in this way:
for (auto it = m.begin(); it != m.end(); ) {
if (it->first == someKey)
m.erase(it++); // use post-increment,pass copy of iterator, advance it
else
++it;
}
and you can do it in similar way while erasing key from Poco::JSON::Object. Where did you read that remove invalidates iterators?
Some snippet code from source:
class JSON_API Object {
typedef std::map<std::string, Dynamic::Var> ValueMap; // <--- map
//...
Iterator begin();
/// Returns begin iterator for values.
Iterator end();
/// Returns end iterator for values.
void remove(const std::string& key);
/// Removes the property with the given key.
ValueMap _values; // <---
};
inline Object::Iterator Object::begin()
{
return _values.begin();
}
inline Object::Iterator Object::end()
{
return _values.end();
}
inline void Object::remove(const std::string& key)
{
_values.erase(key); // <--- erase is called on map, so iteratos are not invalidated
if (_preserveInsOrder)
{
KeyList::iterator it = _keys.begin();
KeyList::iterator end = _keys.end();
for (; it != end; ++it)
{
if (key == (*it)->first)
{
_keys.erase(it);
break;
}
}
}
_modified = true;
}
You could rewrite your loop into:
for(auto it = poco_json->begin();it != poco_json->end();)
{
// do something
if (it->first == "specific key")
{
auto copyIt = it++;
poco_json->remove(copyIt->first);
}
else
++it;
}
EDIT
Why your code doesn't work on range-for loop:
for (auto& objs : *poco_json)
{
// do something
if (objs.first == "specific key")
poco_json->remove(key);
}
it is translated into
for (auto it = poco_json->begin(); it != poco_json->end(); ++it)
{
// do something
if (it->first == "specific key")
poco_json->remove(it->first);
// remove is called, it is erased from inner map
// ++it is called on iterator which was invalidated,
// code crashes
}
You can modify this code in Poco:
inline Iterator Object::remove(const std::string& key)
{
auto ret_it = _values.erase(key);
if (_preserveInsOrder)
{
KeyList::iterator it = _keys.begin();
KeyList::iterator end = _keys.end();
for (; it != end; ++it)
{
if (key == (*it)->first)
{
_keys.erase(it);
break;
}
}
}
_modified = true;
return ret_it;
}
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.
In the below Code-snippet, I am trying to manipulate the contents of each of the lists present in the MAP mp but by returning a pointer to list corresponding map's key whose list needs modification. I am aware that a direct modification of the list contents is possible instead of calling getlist and then modifiying it, but I am new to STL and C++ and trying to learn STL by playing around a bit with Iterators and Lists.
When the below code is executed, a Segmentation fault is thrown at the line "(*lit) = 10". Can anyone help me understand what's going wrong here?
static void getlist(int num,map<int,list<int>> mp, list<int>** l_ptr )
{
map<int,list<int>>::iterator it = mp.begin();
while( it != mp.end())
{
if(it->first == num )
{
*l_ptr = &(it->second);
return;
}
it++;
}
}
int main()
{
map<int,list<int>> mp;
mp[1] = {2,2,2};
mp[2] = {3,3,3};
mp[3] = {4,4,4};
map<int,list<int>>::iterator it = mp.begin();
list<int>::iterator lit;
list<int>* r_l = new list<int>;
//getlist(it->first,mp,r_l);
while( it != mp.end())
{
getlist(it->first,mp,&r_l);
lit = r_l->begin();
while(lit != r_l->end())
{
(*lit) = 10;
lit++;
}
it++;
}
it = mp.begin();
while( it != mp.end())
{
lit = (it->second).begin();
while(lit != (it->second).end())
{
cout<<(*lit);
lit++;
}
it++;
}
return 0;
}
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);
}