Access to specific index in ptree array - c++

I am using boost library to manipulate a JSON file and I would like to access to a specific index of an array in this JSON.
boost::property_tree::ptree& jsonfile;
const boost::property_tree::ptree& array =
jsonfile.get_child("my_array");
What I would like to do is accessing to the value stored at index :
// This code does not compile
int value = array[index].get < int > ("property");

Just code it using the iterators:
template <typename T = std::string>
T element_at(ptree const& pt, std::string name, size_t n) {
return std::next(pt.get_child(name).find(""), n)->second.get_value<T>();
}
If you want to have the index checked for bounds:
template <typename T = std::string>
T element_at_checked(ptree const& pt, std::string name, size_t n) {
auto r = pt.get_child(name).equal_range("");
for (; r.first != r.second && n; --n) ++r.first;
if (n || r.first==r.second)
throw std::range_error("index out of bounds");
return r.first->second.get_value<T>();
}
Demo
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
template <typename T = std::string>
T element_at(ptree const& pt, std::string name, size_t n) {
return std::next(pt.get_child(name).find(""), n)->second.get_value<T>();
}
template <typename T = std::string>
T element_at_checked(ptree const& pt, std::string name, size_t n) {
auto r = pt.get_child(name).equal_range("");
for (; r.first != r.second && n; --n) ++r.first;
if (n || r.first==r.second)
throw std::range_error("index out of bounds");
return r.first->second.get_value<T>();
}
int main() {
ptree pt;
{
std::istringstream iss("{\"a\":[1, 2, 3, 4, 5, 6]}");
read_json(iss, pt);
}
write_json(std::cout, pt, false);
// get the 4th element:
std::cout << element_at_checked(pt, "a", 3) << "\n";
// get it as int
std::cout << element_at_checked<int>(pt, "a", 3) << "\n";
// get non-existent array:
try { std::cout << element_at_checked<int>(pt, "b", 0) << "\n"; } catch(std::exception const& e) { std::cout << e.what() << "\n"; }
try { std::cout << element_at_checked<int>(pt, "a", 6) << "\n"; } catch(std::exception const& e) { std::cout << e.what() << "\n"; }
}
Prints
{"a":["1","2","3","4","5","6"]}
4
4
No such node (b)
index out of bounds

Related

Creating std::set copies only one element, how to fix this?

v_map has the correct amount of information stored, however when i try to use std::set it only copies one element ,I assume the first one. This is my first time using std::set , maybe I miss something here...Thanks for your help !
typedef std::map<std::string,std::pair<int,int>> points_map;
void list_average(points_map &v_map)
{
Comparator compFunctor = [](std::pair<std::string,std::pair<int,int>> elem1,std::pair<std::string,std::pair<int,int>> elem2)
{
std::pair<int,int> it = elem1.second;
std::pair<int,int> jt = elem2.second;
return it.first < jt.first;
};
std::set<std::pair<std::string,std::pair<int,int>>,Comparator> v_set(v_map.begin(),v_map.end(),compFunctor);
for (std::pair<std::string,std::pair<int,int>> it : v_set)
{
std::pair<int,int> jt = it.second;
std::cout << it.first << " " << (jt.second - jt.first) / jt.first<< std::endl;
}
}
Note the following is the full program, I apologize in advance for the ugly code , and length of the code ,also I rewrote the name in the upper part of my code, in the full code , this particular function is called list_atlag
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <codecvt>
#include <iterator>
#include <numeric>
#include <functional>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
#include <boost/tokenizer.hpp>
class Adatok
{
public:
Adatok(std::string name, std::string path, std::string date, int points) : _name(name), _path(path), _date(date), _points(points) {}
Adatok(const Adatok &other) = default;
Adatok &operator=(const Adatok &other) = default;
std::string get_name() { return _name; }
std::string get_path() { return _path; }
std::string get_date() { return _date; }
int get_points() { return _points; }
private:
std::string _name;
std::string _path;
std::string _date;
int _points;
};
class Ranglista
{
public:
Ranglista(std::string name, int points) : _name(name), _points(points) {}
Ranglista(const Ranglista &other) = default;
Ranglista &operator=(const Ranglista &other) = default;
std::string get_name() { return _name; }
int get_points() { return _points; }
bool operator<(const Ranglista &other)
{
return _points > other._points;
}
private:
std::string _name;
int _points;
};
class Vedes
{
public:
Vedes(std::string name, int point) : _name(name), _point(point) { _count++; }
Vedes(const Vedes &other) = default;
Vedes &operator=(const Vedes &other) = default;
std::string get_name() { return _name; }
int get_point() { return _point; }
int get_count() { return _count; }
void set_stuff(int &points)
{
_point += points;
_count++;
}
bool operator<(const Vedes &other)
{
return _count > other._count;
}
private:
std::string _name;
int _point;
int _count = 0;
};
typedef std::map<std::string, int> path_value; //minden path + az erteke
typedef std::vector<Adatok> name_path_date; //bejegyzesek
typedef std::vector<Ranglista> ranglista; //ranglista
typedef std::map<std::string,std::pair<int,int>> vedes_vec; //vedesek
typedef std::function<bool(std::pair<std::string,std::pair<int,int>>,std::pair<std::string,std::pair<int,int>>)> Comparator;
void create_pv(path_value &, boost::filesystem::path); //feltolti a path+ertek map-ot
void create_npd(name_path_date &, path_value &, std::string input); //feltolti a bejegyzesek vektorat + mindenki pontszama map
void create_np(name_path_date &, path_value &); // name + path map
void list_np(path_value &name_point); // nam + path kiiratas
void list_bejegyzesek(name_path_date &bejegyzesek); // bejegyzesek vektora kiiratas
bool check_bejegyzesek(name_path_date &bejegyzesek, std::string name, std::string path); //van-e mar ilyen bejegyzes
void create_rl(ranglista &rl_vec, path_value &name_point); //ranglista feltoltes
void list_rl(ranglista &rl_vec); //ranglista kiiratas
void vedes_atlag(name_path_date &bejegyzesek, vedes_vec &v_vec); //vedes atlag map
void list_atlag(vedes_vec &v_vec); //vedes atlag kiiratas
bool check_vedes(vedes_vec &v_vec, std::string name);
void vedes_elem(vedes_vec &v_vec, std::string name, int &&points); //
//void accumulate_pv(path_value&);
int main(int argc, char **argv)
{
std::vector<std::string> roots = {"City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/", "City/Debrecen/Oktatás/Informatika/Programozás/DEIK/"};
std::string input_file_name = "db-2018-05-06.csv";
/* OPTIONS */
boost::program_options::options_description desc("ALLOWED OPTIONS");
desc.add_options()("help", "help msg")("root,r", boost::program_options::value<std::vector<std::string>>())("csv", boost::program_options::value<std::string>(), "comma separated values")("rank", "rang lista")("vedes", "labor vedesek");
boost::program_options::positional_options_description pdesc;
pdesc.add("root", -1);
boost::program_options::variables_map vm;
boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(pdesc).run(), vm);
boost::program_options::notify(vm);
int sum = 0;
path_value pv_map;
if (vm.count("help") || argc == 1)
{
std::cout << desc << std::endl;
return 1;
}
if (vm.count("root"))
{
roots = vm["root"].as<std::vector<std::string>>();
for (auto &i : roots)
{
boost::filesystem::path path(i);
create_pv(pv_map, path);
}
for (path_value::iterator it{pv_map.begin()}; it != pv_map.end(); it++)
sum += it->second;
//std::cout << sum << std::endl;create_npd
std::cout << std::accumulate(pv_map.begin(), pv_map.end(), 0, [](int value, const std::map<std::string, int>::value_type &p) { return value + p.second; });
std::cout << std::endl;
}
if (vm.count("csv"))
{
//input_file_name = vm["csv"].as<std::string>();
std::ifstream input_file{vm["csv"].as<std::string>()};
name_path_date bejegyzesek;
std::string temp;
path_value name_point;
while (getline(input_file, temp))
create_npd(bejegyzesek, pv_map, temp);
create_np(bejegyzesek, name_point);
//list_bejegyzesek(bejegyzesek);
//list_np(name_point);
if (vm.count("rank"))
{
ranglista rl_vec;
create_rl(rl_vec, name_point);
list_rl(rl_vec);
}
if (vm.count("vedes"))
{
vedes_vec v_vec;
vedes_atlag(bejegyzesek, v_vec);
list_atlag(v_vec);
}
return 0;
}
return 0;
}
void create_pv(path_value &pv_map, boost::filesystem::path path)
{
boost::filesystem::directory_iterator it{path}, eod;
BOOST_FOREACH (boost::filesystem::path const &p, std::make_pair(it, eod))
{
if (boost::filesystem::is_regular_file(p))
{
boost::filesystem::ifstream regular_file{p};
std::string temp;
int sum = 0; //aktualis .props erteke
while (getline(regular_file, temp))
{
temp.erase(0, temp.find_last_of('/'));
temp.erase(0, temp.find_first_of(' '));
sum += std::atoi((temp.substr(temp.find_first_of("0123456789"), temp.find_last_of("0123456789"))).c_str());
}
std::string result = p.string();
std::string result_path = result.substr(0, result.find_last_of('/'));
//std::cout << result_path << std::endl;
//pv_map.insert(std::make_pair(result, sum));
pv_map[result_path] = sum;
}
else
create_pv(pv_map, p);
}
}
//void accumulate_pv(path_value& pv_map)
//{
// std::cout<<std::accumulate(pv_map.begin(),pv_map.end(),0,[](int value,const path_value::int& p){return value+p.second;});
//}
void create_npd(name_path_date &bejegyzesek, path_value &pv_map, std::string input)
{
boost::tokenizer<boost::escaped_list_separator<char>> tokenizer{input};
boost::tokenizer<boost::escaped_list_separator<char>>::iterator it{tokenizer.begin()};
std::string name = *it;
std::string path = *(++it);
std::string date = *(++it);
path = path.substr(2);
if (!check_bejegyzesek(bejegyzesek, name, path))
bejegyzesek.push_back(Adatok(name, path, date, pv_map["/home/erik/Documents/Programs/"+path]));
}
bool check_bejegyzesek(name_path_date &bejegyzesek, std::string name, std::string path)
{
bool ok = false;
for (name_path_date::iterator it{bejegyzesek.begin()}; it != bejegyzesek.end(); it++)
{
if ((it->get_name() == name) && (it->get_path() == path))
ok = true;
}
return ok;
}
bool check_vedes(vedes_vec &v_vec, std::string name)
{
vedes_vec::iterator it = v_vec.find(name);
if (it != v_vec.end()) return true;
else return false;
}
void vedes_elem(vedes_vec &v_vec, std::string name, int &&points)
{
/*for (auto &it : v_vec)
if (it.get_name() == name)
it.set_stuff(points);
*/
vedes_vec::iterator i = v_vec.find(name);
std::pair<int,int> it = i->second;
//auto& jt = it->second;
it.first++;
it.second += points;
}
void create_np(name_path_date &bejegyzesek, path_value &name_point)
{
for (name_path_date::iterator it{bejegyzesek.begin()}; it != bejegyzesek.end(); it++)
if (name_point.count(it->get_name()) == 0)
name_point.insert(std::make_pair(it->get_name(), it->get_points()));
else
name_point[it->get_name()] += it->get_points();
}
void list_np(path_value &name_point)
{
for (path_value::iterator it{name_point.begin()}; it != name_point.end(); it++)
{
if (it->second)
std::cout << it->first << " " << it->second << std::endl;
}
}
void list_bejegyzesek(name_path_date &bejegyzesek)
{
for (name_path_date::iterator it{bejegyzesek.begin()}; it != bejegyzesek.end(); it++)
if (it->get_name() == "Varga Erik")
std::cout << it->get_name() << " " << it->get_path() << " " << it->get_points() << std::endl;
}
void create_rl(ranglista &rl_vec, path_value &name_point)
{
for (auto &it : name_point)
{
if (it.second > 0)
rl_vec.push_back(Ranglista(it.first, it.second));
}
std::sort(rl_vec.begin(), rl_vec.end());
}
void list_rl(ranglista &rl_vec)
{
for (auto &it : rl_vec)
std::cout << it.get_name() << " " << it.get_points() << std::endl;
}
void vedes_atlag(name_path_date &bejegyzesek, vedes_vec &v_vec)
{
std::string key = "City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/Labor/Védés/";
for (auto &it : bejegyzesek)
{
if ((it.get_path().find("City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/Labor/Védés/") != std::string::npos) && (it.get_points()) && (!check_vedes(v_vec, it.get_name())))
v_vec.insert(std::make_pair(it.get_name(),std::make_pair(1,it.get_points())));
else if ((check_vedes(v_vec, it.get_name())) && (it.get_path().find("City/Debrecen/Oktatás/Informatika/Programozás/DEIK/Prog1/Labor/Védés/") != std::string::npos) && (it.get_points()))
vedes_elem(v_vec, it.get_name(), it.get_points());
}
}
void list_atlag(vedes_vec &v_vec)
{
//std::sort(v_vec.begin(), v_vec.end());
Comparator compFunctor = [](std::pair<std::string,std::pair<int,int>> elem1,std::pair<std::string,std::pair<int,int>> elem2)
{
std::pair<int,int> it = elem1.second;
std::pair<int,int> jt = elem2.second;
return it.first < jt.first;
};
std::set<std::pair<std::string,std::pair<int,int>>,Comparator> v_set(v_vec.begin(),v_vec.end(),compFunctor);
//int sum = 0;
//int csum = 0;
for (std::pair<std::string,std::pair<int,int>> it : v_set)
{
std::pair<int,int> jt = it.second;
std::cout << it.first << " " << (jt.second - jt.first) / jt.first<< std::endl;
//sum += it.get_point();
//csum += it.get_count();
//sum = std::accumulate(v_vec.begin(), v_vec.end(), 0, [](int i, Vedes &o) { return i + o.get_point(); });
//csum = std::accumulate(v_vec.begin(), v_vec.end(), 0, [](int i, Vedes &o) { return i + o.get_count(); });
}
//std::cout << (sum - csum) / csum << std::endl;
}
so, as described here
template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;
std::set is an associative container that contains a sorted set of unique objects of type Key.
I cleaned up your code, and made a Minimal, Complete, and Verifiable example,
#include <iostream>
#include <map>
#include <set>
using point_pair = std::pair<int,int>;
using points_map = std::map<std::string, point_pair>;
using points_set_pair = std::pair<std::string, point_pair>;
auto compFunctor = [](const points_set_pair &elem1, const points_set_pair &elem2)
{
return elem1.second.first < elem2.second.first;
};
using points_set = std::set<points_set_pair, decltype(compFunctor)>;
void list_average(const points_map &v_map)
{
points_set v_set(v_map.begin(),v_map.end(),compFunctor);
for (auto &elem : v_set)
{
const point_pair &jt = elem.second;
std::cout << elem.first << " " << (jt.second - jt.first) / jt.first<< "\n";
}
}
Now consider the first version of main
int main()
{
points_map v_map = { {"foo", { 1, 2}}, {"bar", { 3, 4}}};
list_average(v_map);
}
output:
foo 1
bar 0
Now consider the second version of main:
int main()
{
points_map v_map = { {"foo", { 1, 2}}, {"bar", { 1, 4}}};
list_average(v_map);
}
output:
bar 3
See the problem? As .second.first of the elements are both 1, the latter replaces the first. It is not unique. That's the downside of std::set.
So, what then?
Don't use std::set, but use std::vector and std::sort. Example:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using point_pair = std::pair<int,int>;
using points_map = std::map<std::string, point_pair>;
using string_point_pair = std::pair<std::string, point_pair>;
auto compFunctor = [](string_point_pair const &elem1, string_point_pair const &elem2)
{
return
elem1.second.first != elem2.second.first?
elem1.second.first < elem2.second.first:
elem1.second.second < elem2.second.second;
};
void list_average(points_map const &v_map)
{
std::vector<string_point_pair> v_vec(v_map.begin(),v_map.end());
std::sort(v_vec.begin(), v_vec.end(), compFunctor);
for (auto &elem : v_vec)
{
const point_pair &jt = elem.second;
std::cout << elem.first << " " << (jt.second - jt.first) / jt.first<< "\n";
}
}
int main()
{
points_map v_map = { {"foo", { 1, 2}}, {"bar", { 1, 4}}, {"baz", { 2, 4}}};
list_average(v_map);
}
Output:
foo 1
bar 3
baz 1
live demo

C++ Find and save duplicates in vector

I have a custom vector of my user defined type vector
First vector gets filled with elements through stdin, then i sort it and try to find duplicates in it and save them
i've managed to find all unique elements, but i need to find and get a vector of duplicates
I need a hint or a simple solution for this problem
here's my code below:
Agressor.h
#ifndef Agressor_h
#define Agressor_h
#include <string>
#include <vector>
using namespace std;
class Agressor{
public:
/*const char**/ string traderIdentifier;
/*const char**/ string side;
int quantity;
int price;
vector<Agressor> v;
void display(){
cout << traderIdentifier << " " << side << " " << quantity << " " << price << endl;
}
explicit Agressor(){
}
~Agressor(){
}
friend ostream &operator<<(ostream& stream, const Agressor& item);
const friend bool operator > (const Agressor &a1, const Agressor &a2);
// const friend bool operator == (const Agressor &a1, const Agressor &a2);
/* vector<Agressor>& operator[](int i ){
return v[i];
}*/
};
ostream &operator<<(ostream& stream, const Agressor& item) {
string side = "";
if(item.side == "B"){
side = '+';
}else{
if(item.side == "S"){
side = "-";
}
}
stream << item.traderIdentifier << side << item.quantity << "#" << item.price << "\n";
return stream;
}
const bool operator == (const Agressor &a1, const Agressor &a2){
bool isEqual = false;
if((a1.price*a1.quantity == a2.price*a2.quantity) && (a1.traderIdentifier == a2.traderIdentifier) && (a1.side == a2.side)){
isEqual = true;
}
return(isEqual);
}
const bool operator > (const Agressor &a1, const Agressor &a2){
bool isGreater = false;
if(a1.price*a1.quantity > a2.price*a2.quantity){
isGreater = true;
}
return(isGreater);
}
#endif /* Agressor_h */
main.cpp
#include <iostream>
#include "Agressor.h"
#include <sstream>
using namespace std;
vector<string> &split(const string &s, char delim, vector<string> &elems)
{
stringstream ss(s);
string item;
while (getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
vector<string> split(const string &s, char delim)
{
vector<string> elems;
split(s, delim, elems);
return elems;
}
bool equal_comp(const Agressor& a1, const Agressor& a2){
if((a1.price*a1.quantity == a2.price*a2.quantity) && (a1.traderIdentifier == a2.traderIdentifier) && (a1.side == a2.side)){
return true;
}
return false;
}
int main(int argc, const char * argv[]) {
Agressor agr;
while (true) {
std::string sText;
cout << "enter query:" << endl;
std::getline(std::cin, sText);
if(sText == "q"){
cout << "Program terminated by user" << endl;
break;
}else{
std::vector<std::string> sWords = split(sText, ' ');
agr.traderIdentifier = sWords[0];
agr.side = sWords[1];
agr.quantity = stoi(sWords[2]);
agr.price = stoi(sWords[3]);
agr.v.push_back(agr);
vector<Agressor>::iterator it;
sort(agr.v.begin(), agr.v.end(), greater<Agressor>());
//unique(agr.v.begin(), agr.v.end(), equal_comp);
for (vector<Agressor>::const_iterator i = agr.v.begin(); i != agr.v.end(); ++i)
cout << *i << ' ';
}
}
cout << "here we go..." << endl;
vector<Agressor>::iterator it;
sort(agr.v.begin(), agr.v.end(), greater<Agressor>());
//it = unique(agr.v.begin(),agr.v.end(), equal_comp);
//agr.v.resize( distance(agr.v.begin(),it) );
agr.v.erase(unique(agr.v.begin(),agr.v.end(), equal_comp), agr.v.end());
copy(agr.v.begin(), agr.v.end(), ostream_iterator<Agressor>(cout, "\n"));
return 0;
}
You might use something like:
template <typename T>
std::vector<T> get_duplicates(const std::vector<T>& v)
{
// expect sorted vector
auto it = v.begin();
auto end = v.end();
std::vector<T> res;
while (it != end) {
it = std::adjacent_find(it, end);
if (it != end) {
++it;
res.push_back(*it);
}
}
return res;
}
std::unique overwrites duplicate values with later non-duplicate values. You can implement a similar algorithm that moves the values to somewhere.
template<class ForwardIt, class OutputIt, class BinaryPredicate>
ForwardIt unique_retain(ForwardIt first, ForwardIt last, OutputIt d_first, BinaryPredicate p)
{
if (first == last)
return last;
ForwardIt result = first;
while (++first != last) {
if (!p(*result, *first) && ++result != first) {
*d_first++ = std::move(*result);
*result = std::move(*first);
}
}
return ++result;
}
(adapted from this possible implementation of std::unique)
You would then use it like
vector<Agressor> dups;
sort(agr.v.begin(), agr.v.end(), greater<Agressor>());
auto it = unique_retain(agr.v.begin(),agr.v.end(), std::back_inserter(dups), equal_comp);
agr.v.erase(it, agr.v.end());

Type safe index values for std::vector

I have classes that collect index values from different constant STL vectors. Problem is, even if these vectors are different in content and they have different purposes, their indexes are of type std::size_t, so one might erroneusly use the index stored for one vector to access the elements of another vector. Can the code be changed in order to have a compile time error when a index is not used with the correct vector?
A code example:
#include <iostream>
#include <string>
#include <vector>
struct Named
{
std::string name;
};
struct Cat : Named { };
struct Dog : Named { };
struct Range
{
std::size_t start;
std::size_t end;
};
struct AnimalHouse
{
std::vector< Cat > cats;
std::vector< Dog > dogs;
};
int main( )
{
AnimalHouse house;
Range cat_with_name_starting_with_a;
Range dogs_with_name_starting_with_b;
// ...some initialization code here...
for( auto i = cat_with_name_starting_with_a.start;
i < cat_with_name_starting_with_a.end;
++i )
{
std::cout << house.cats[ i ].name << std::endl;
}
for( auto i = dogs_with_name_starting_with_b.start;
i < dogs_with_name_starting_with_b.end;
++i )
{
// bad copy paste but no compilation error
std::cout << house.cats[ i ].name << std::endl;
}
return 0;
}
Disclaimer: please do not focus too much on the example itself, I know it is dumb, it is just to get the idea.
Here is an attempt following up on my comment.
There are of course a lot of room to change the details of how this would work depending on the use-case, this way seemed reasonable to me.
#include <iostream>
#include <vector>
template <typename T>
struct Range {
Range(T& vec, std::size_t start, std::size_t end) :
m_vector(vec),
m_start(start),
m_end(end),
m_size(end-start+1) {}
auto begin() {
auto it = m_vector.begin();
std::advance(it, m_start);
return it;
}
auto end() {
auto it = m_vector.begin();
std::advance(it, m_end + 1);
return it;
}
std::size_t size() {
return m_size;
}
void update(std::size_t start, std::size_t end) {
m_start = start;
m_end = end;
m_size = end - start + 1;
}
Range copy(T& other_vec) {
return Range(other_vec, m_start, m_end);
}
typename T::reference operator[](std::size_t index) {
return m_vector[m_start + index];
}
private:
T& m_vector;
std::size_t m_start, m_end, m_size;
};
// This can be used if c++17 is not supported, to avoid
// having to specify template parameters
template <typename T>
Range<T> make_range(T& t, std::size_t start, std::size_t end) {
return Range<T>(t, start, end);
}
int main() {
std::vector<int> v1 {1, 2, 3, 4, 5};
std::vector<double> v2 {0.5, 1., 1.5, 2., 2.5};
Range more_then_2(v1, 1, 4); // Only works in c++17 or later
auto more_then_1 = make_range(v2, 2, 4);
for (auto v : more_then_2)
std::cout << v << ' ';
std::cout << std::endl;
for (auto v : more_then_1)
std::cout << v << ' ';
std::cout << std::endl;
more_then_2.update(2,4);
for (auto v : more_then_2)
std::cout << v << ' ';
std::cout << std::endl;
auto v3 = v1;
auto more_then_2_copy = more_then_2.copy(v3);
for (unsigned i=0; i < more_then_2_copy.size(); ++i)
std::cout << more_then_2_copy[i] << ' ';
return 0;
}

Replace the loops to insert and update a collection by the Standard Library

I have two loops to iterate a collection:
the first one adds missing elements into the collection.
the second one updates existing instances into the collection.
How can I replace the loops by standard library functions?
// add missing elements into the collection (if any)
for (auto i = collection.size(); i < objectTypes.size() + startIdx; i++)
{
collection.push_back(CNode(i));
}
// update elements of the collection
for (const auto& objectType : objectTypes)
{
collection[startIdx++].SetObjectType(objectType);
}
This question is a further step to this one.
Here a complete sample that compiles:
#include <string>
#include <iostream>
#include <vector>
#include <regex>
class CObject
{
std::string _objectType;
public:
CObject() : _objectType("n/a") {}
void SetObjectType(std::string objectType) { _objectType = objectType; }
std::string GetObjectType() const { return _objectType; }
};
class CNode
{
int _id;
CObject _object;
public:
explicit CNode(int id) : _id(id) {}
void SetObjectType(std::string objectType) { _object.SetObjectType(objectType); }
std::string GetObjectType() const { return _object.GetObjectType(); }
};
std::vector<std::string> SplitLine(std::string const& line, std::string seps)
{
std::regex regxSeps(seps); // the dot character needs to be escaped in a regex
std::sregex_token_iterator rit(line.begin(), line.end(), regxSeps, -1);
return std::vector<std::string>(rit, std::sregex_token_iterator());
}
static int ParseLine(std::string line, std::string seps, size_t startIdx, std::vector<CNode>& collection)
{
if (startIdx > collection.size())
{
throw std::invalid_argument("the start index is out of range");
}
auto objectTypes = SplitLine(line, seps);
for (auto missingIdx = collection.size(); missingIdx < objectTypes.size() + startIdx; missingIdx++)
{
collection.push_back(CNode(missingIdx));
}
for (const auto& objectType : objectTypes)
{
collection[startIdx++].SetObjectType(objectType);
}
return (startIdx - 1);
}
int main()
{
std::string seps = "\\."; // the dot character needs to be escaped in a regex
// 2 3 4 5 6 7 8 9
std::string line = "abc.def.ghi.klm.nop.qrs.tuv.wxyz";
std::vector<CNode> collection{ CNode(0), CNode(1), CNode(2) , CNode(3) , CNode(4) , CNode(5) };
auto startAt = 2;
try
{
auto collection_size = ParseLine(line, seps, startAt, collection);
std::cout << collection_size << std::endl;
for (auto value : collection)
{
std::cout << value.GetObjectType() << std::endl;
}
}
catch (std::invalid_argument& e)
{
std::cout << " out of range exception " << e.what() << std::endl;
}
}
Perhaps something similar to this would work (I have not tested it):
auto i = collection.size();
std::transform (objectTypes.begin(), objectTypes.end(),
std::back_inserter(collection),
[&](const ObjectType& ot) {
CNode ct(++i);
ct.SetObjectType(ot);
return ct;
});
Is there any reason also not to add an objectType parameter to the CNode constructor?
In case someone is interested to get the complete code of the function, below the final solution using the standard library to replace the two loops to insert and update the collection:
// Compute the number of elements to insert and to update
auto numInserts = startIdx + objectTypes.size() - collection.size();
auto numUpdates = collection.size() - startIdx;
// update the elements that already exists in the collection
std::for_each( objectTypes.begin(),
objectTypes.begin() + numUpdates,
[&](const std::string objectType)
{ collection[startIdx++].SetObjectType(objectType); });
// add the missing elements into the collection (if any)
std::transform( objectTypes.end() - numInserts,
objectTypes.end(),
std::back_inserter(collection),
[&](const std::string& objectType)
{ return CNode(++startIdx, objectType); });
Here a complete sample that compiles:
#include <string>
#include <iostream>
#include <vector>
#include <regex>
class CObject
{
std::string _objectType;
public:
CObject() : _objectType("n/a") {}
explicit CObject(std::string objectType) : _objectType(objectType) {};
void SetObjectType(std::string objectType) { _objectType = objectType; }
std::string GetObjectType() const { return _objectType; }
};
class CNode
{
int _id;
CObject _object;
public:
explicit CNode(int id) : _id(id) {}
explicit CNode(int id, std::string objectType) : _id(id), _object(objectType) {}
void SetObjectType(std::string objectType) { _object.SetObjectType(objectType); }
std::string GetObjectType() const { return _object.GetObjectType(); }
};
std::vector<std::string> SplitLine(std::string const& line, std::string seps)
{
std::regex regxSeps(seps); // the dot character needs to be escaped in a regex
std::sregex_token_iterator rit(line.begin(), line.end(), regxSeps, -1);
return std::vector<std::string>(rit, std::sregex_token_iterator());
}
static int ParseLineWithLoops(std::string line, std::string seps, size_t startIdx, std::vector<CNode>& collection)
{
if (startIdx > collection.size())
{
throw std::invalid_argument("the start index is out of range");
}
auto objectTypes = SplitLine(line, seps);
// expand the collection if needed
for (auto idx = collection.size(); idx < objectTypes.size() + startIdx; idx++)
{
collection.push_back(CNode(idx));
}
// update the types of elements into the collection
for (const auto& objectType : objectTypes)
{
collection[startIdx++].SetObjectType(objectType);
}
return (startIdx - 1);
}
static int ParseLineWithStdTransform(std::string line, std::string seps, size_t startIdx, std::vector<CNode>& collection)
{
if (startIdx > collection.size())
{
throw std::invalid_argument("the start index is out of range");
}
auto objectTypes = SplitLine(line, seps);
// Compute the number of elements to insert and to update
auto numInserts = startIdx + objectTypes.size() - collection.size();
auto numUpdates = collection.size() - startIdx;
// update the elements that already exists in the collection
std::for_each( objectTypes.begin(),
objectTypes.begin() + numUpdates,
[&](const std::string objectType) { collection[startIdx++].SetObjectType(objectType); });
// add the missing elements into the collection (if any)
std::transform( objectTypes.end() - numInserts,
objectTypes.end(),
std::back_inserter(collection),
[&](const std::string& objectType) { return CNode(++startIdx, objectType); });
return (collection.size() - 1);
}
int main()
{
std::string seps = "\\."; // the dot character needs to be escaped in a regex
// 2 3 4 5 6 7 8 9
std::string line = "abc.def.ghi.klm.nop.qrs.tuv.wxyz";
auto startAt = 2;
std::vector<CNode> collection1{ CNode(0), CNode(1), CNode(2) , CNode(3) , CNode(4) , CNode(5) };
try
{
auto collection_size = ParseLineWithStdTransform(line, seps, startAt, collection1);
std::cout << collection_size << std::endl;
for (auto value : collection1)
{
std::cout << value.GetObjectType() << std::endl;
}
}
catch (std::invalid_argument& e)
{
std::cout << " out of range exception " << e.what() << std::endl;
}
std::vector<CNode> collection2{ CNode(0), CNode(1), CNode(2) , CNode(3) , CNode(4) , CNode(5) };
try
{
auto collection_size = ParseLineWithLoops(line, seps, startAt, collection2);
std::cout << collection_size << std::endl;
for (auto value : collection2)
{
std::cout << value.GetObjectType() << std::endl;
}
}
catch (std::invalid_argument& e)
{
std::cout << " out of range exception " << e.what() << std::endl;
}
}

How to index and query STL map containers by multiple keys?

I came across one requirement where the record is stored as
Name : Employee_Id : Address
where Name and Employee_Id are supposed to be keys that is, a search function is to be provided on both Name and Employee Id.
I can think of using a map to store this structure
std::map< std:pair<std::string,std::string> , std::string >
// < < Name , Employee-Id> , Address >
but I'm not exactly sure how the search function will look like.
Boost.Multiindex
This is a Boost example
In the above example an ordered index is used but you can use also a hashed index:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <string>
#include <iostream>
struct employee
{
int id_;
std::string name_;
std::string address_;
employee(int id,std::string name,std::string address):id_(id),name_(name),address_(address) {}
};
struct id{};
struct name{};
struct address{};
struct id_hash{};
struct name_hash{};
typedef boost::multi_index_container<
employee,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<boost::multi_index::tag<id>, BOOST_MULTI_INDEX_MEMBER(employee,int,id_)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,std::string,name_)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<address>, BOOST_MULTI_INDEX_MEMBER(employee,std::string,address_)>,
boost::multi_index::hashed_unique<boost::multi_index::tag<id_hash>, BOOST_MULTI_INDEX_MEMBER(employee,int,id_)>,
boost::multi_index::hashed_unique<boost::multi_index::tag<name_hash>, BOOST_MULTI_INDEX_MEMBER(employee,std::string,name_)>
>
> employee_set;
typedef boost::multi_index::index<employee_set,id>::type employee_set_ordered_by_id_index_t;
typedef boost::multi_index::index<employee_set,name>::type employee_set_ordered_by_name_index_t;
typedef boost::multi_index::index<employee_set,name_hash>::type employee_set_hashed_by_name_index_t;
typedef boost::multi_index::index<employee_set,id>::type::const_iterator employee_set_ordered_by_id_iterator_t;
typedef boost::multi_index::index<employee_set,name>::type::const_iterator employee_set_ordered_by_name_iterator_t;
typedef boost::multi_index::index<employee_set,id_hash>::type::const_iterator employee_set_hashed_by_id_iterator_t;
typedef boost::multi_index::index<employee_set,name_hash>::type::const_iterator employee_set_hashed_by_name_iterator_t;
int main()
{
employee_set employee_set_;
employee_set_.insert(employee(1, "Employer1", "Address1"));
employee_set_.insert(employee(2, "Employer2", "Address2"));
employee_set_.insert(employee(3, "Employer3", "Address3"));
employee_set_.insert(employee(4, "Employer4", "Address4"));
// search by id using an ordered index
{
const employee_set_ordered_by_id_index_t& index_id = boost::multi_index::get<id>(employee_set_);
employee_set_ordered_by_id_iterator_t id_itr = index_id.find(2);
if (id_itr != index_id.end() ) {
const employee& tmp = *id_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by non existing id using an ordered index
{
const employee_set_ordered_by_id_index_t& index_id = boost::multi_index::get<id>(employee_set_);
employee_set_ordered_by_id_iterator_t id_itr = index_id.find(2234);
if (id_itr != index_id.end() ) {
const employee& tmp = *id_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an ordered index
{
const employee_set_ordered_by_name_index_t& index_name = boost::multi_index::get<name>(employee_set_);
employee_set_ordered_by_name_iterator_t name_itr = index_name.find("Employer3");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an hashed index
{
employee_set_hashed_by_name_index_t& index_name = boost::multi_index::get<name_hash>(employee_set_);
employee_set_hashed_by_name_iterator_t name_itr = index_name.find("Employer4");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an hashed index but the name does not exists in the container
{
employee_set_hashed_by_name_index_t& index_name = boost::multi_index::get<name_hash>(employee_set_);
employee_set_hashed_by_name_iterator_t name_itr = index_name.find("Employer46545");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
return 0;
}
If you want to use std::map, you can have two separate containers, each one having adifferent key (name, emp id) and the value should be a pointer the structure, so that you will not have multiple copies of the same data.
Example with tew keys:
#include <memory>
#include <map>
#include <iostream>
template <class KEY1,class KEY2, class OTHER >
class MultiKeyMap {
public:
struct Entry
{
KEY1 key1;
KEY2 key2;
OTHER otherVal;
Entry( const KEY1 &_key1,
const KEY2 &_key2,
const OTHER &_otherVal):
key1(_key1),key2(_key2),otherVal(_otherVal) {};
Entry() {};
};
private:
struct ExtendedEntry;
typedef std::shared_ptr<ExtendedEntry> ExtendedEntrySptr;
struct ExtendedEntry {
Entry entry;
typename std::map<KEY1,ExtendedEntrySptr>::iterator it1;
typename std::map<KEY2,ExtendedEntrySptr>::iterator it2;
ExtendedEntry() {};
ExtendedEntry(const Entry &e):entry(e) {};
};
std::map<KEY1,ExtendedEntrySptr> byKey1;
std::map<KEY2,ExtendedEntrySptr> byKey2;
public:
void del(ExtendedEntrySptr p)
{
if (p)
{
byKey1.erase(p->it1);
byKey2.erase(p->it2);
}
}
void insert(const Entry &entry) {
auto p=ExtendedEntrySptr(new ExtendedEntry(entry));
p->it1=byKey1.insert(std::make_pair(entry.key1,p)).first;
p->it2=byKey2.insert(std::make_pair(entry.key2,p)).first;
}
std::pair<Entry,bool> getByKey1(const KEY1 &key1)
{
const auto &ret=byKey1[key1];
if (ret)
return std::make_pair(ret->entry,true);
return std::make_pair(Entry(),false);
}
std::pair<Entry,bool> getByKey2(const KEY2 &key2)
{
const auto &ret=byKey2[key2];
if (ret)
return std::make_pair(ret->entry,true);
return std::make_pair(Entry(),false);
}
void deleteByKey1(const KEY1 &key1)
{
del(byKey1[key1]);
}
void deleteByKey2(const KEY2 &key2)
{
del(byKey2[key2]);
}
};
int main(int argc, const char *argv[])
{
typedef MultiKeyMap<int,std::string,int> M;
M map1;
map1.insert(M::Entry(1,"aaa",7));
map1.insert(M::Entry(2,"bbb",8));
map1.insert(M::Entry(3,"ccc",9));
map1.insert(M::Entry(7,"eee",9));
map1.insert(M::Entry(4,"ddd",9));
map1.deleteByKey1(7);
auto a=map1.getByKey1(2);
auto b=map1.getByKey2("ddd");
auto c=map1.getByKey1(7);
std::cout << "by key1=2 (should be bbb ): "<< (a.second ? a.first.key2:"Null") << std::endl;
std::cout << "by key2=ddd (should be ddd ): "<< (b.second ? b.first.key2:"Null") << std::endl;
std::cout << "by key1=7 (does not exist): "<< (c.second ? c.first.key2:"Null") << std::endl;
return 0;
}
Output:
by key1=2 (should be bbb ): bbb
by key2=ddd (should be ddd ): ddd
by key1=7 (does not exist): Null
If EmployeeID is the unique identifier, why use other keys? I would use EmployeeID as the internal key everywhere, and have other mappings from external/human readable IDs (such as Name) to it.
C++14 std::set::find non-key searches solution
This method saves you from storing the keys twice, once one the indexed object and secondly on as the key of a map as done at: https://stackoverflow.com/a/44526820/895245
This provides minimal examples of the central technique that should be easier to understand first: How to make a C++ map container where the key is part of the value?
#include <cassert>
#include <set>
#include <vector>
struct Point {
int x;
int y;
int z;
};
class PointIndexXY {
public:
void insert(Point *point) {
sx.insert(point);
sy.insert(point);
}
void erase(Point *point) {
sx.insert(point);
sy.insert(point);
}
Point* findX(int x) {
return *(this->sx.find(x));
}
Point* findY(int y) {
return *(this->sy.find(y));
}
private:
struct PointCmpX {
typedef std::true_type is_transparent;
bool operator()(const Point* lhs, int rhs) const { return lhs->x < rhs; }
bool operator()(int lhs, const Point* rhs) const { return lhs < rhs->x; }
bool operator()(const Point* lhs, const Point* rhs) const { return lhs->x < rhs->x; }
};
struct PointCmpY {
typedef std::true_type is_transparent;
bool operator()(const Point* lhs, int rhs) const { return lhs->y < rhs; }
bool operator()(int lhs, const Point* rhs) const { return lhs < rhs->y; }
bool operator()(const Point* lhs, const Point* rhs) const { return lhs->y < rhs->y; }
};
std::set<Point*, PointCmpX> sx;
std::set<Point*, PointCmpY> sy;
};
int main() {
std::vector<Point> points{
{1, -1, 1},
{2, -2, 4},
{0, 0, 0},
{3, -3, 9},
};
PointIndexXY idx;
for (auto& point : points) {
idx.insert(&point);
}
Point *p;
p = idx.findX(0);
assert(p->y == 0 && p->z == 0);
p = idx.findX(1);
assert(p->y == -1 && p->z == 1);
p = idx.findY(-2);
assert(p->x == 2 && p->z == 4);
}