Table Representation in C++ - c++

Basically, say, I have the following data:
(let me note that the columns change with every piece of data I get, i.e. I need to keep things general and cannot restrict my solution to only Tenor, Date, etc.)
Now I want to be able to represent and conveniently access this data in an object/class in C++.
I have been playing around with map a bit:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class my_table {
private:
map<string, map<string, string>> c;
public:
void set(string key1, string key2, string value){ this->c[key1][key2] = value; }
string get(string key1, string key2){
map<string, map<string, string>>::iterator it = this->c.find(key1);
if (it != this->c.end()){
map<string, string>::iterator it2 = this->c[key1].find(key2);
if (it2 != this->c[key1].end()){
return c[key1][key2];
}
return "n/a";
}
return "n/a";
}
};
void main() {
my_table a;
a.set("1", "Tenor", "1D");
cout << a.get("1", "Tenor") << endl; // returns '1D'
cout << a.get("2", "Tenor") << endl; // returns 'n/a'
cout << a.get("1", "Rate") << endl; // returns 'n/a'
}
But I am not overly satisfied with this implemenation. In particular, I would want to be able to do things like:
a.get("Tenor","3M", "Rate") // should return '1.6%'
a.get("Date","01-Jan-2016", "Responsibility") // should return 'MG'
a.get_all("Type","Forward", "Rate") // should return an array {1.3%,2.4%}
a.get_row(4) // should return an array {4M,...,2.0%,MG}
And:
I am wondering whether there are there any standard packages that could help me simplify this implementation overall?
In particular, my get function seems unnecessarily cumbersome.
And generally, is map is even the right way to go in terms of storing data like this?
And what if I wanted to generalise this implemenation to more than just 2 keys? Maybe 3 keys. My solution is quite rigid

enum struct Type {
Spot
Forward
}
struct Row {
string tenor;
Date date;
int convention;
Type type;
double rate;
ResposibilityType responsibility;
};
std::vector<Row> table = {
[...]
}
access you do with std::find_if. Tables in databases might be stored like this internally. If you want multiple primary keys you can create for each key a map that maps from the primary key to an element in table. If you want a combined key, you need tuple like this std::map<std::pair<Key1,Key2>, Row*>

How about the matrix type from boost.ublas? You can create a simple enum type to easily reference columns.
For querying you can probably build something quick via the filter_iterator.
Hope this helps!
Edit: Sorry didn't notice your comment. A quick hack I can think of to support dynamic column size is using a hash map for storing column name to column index mapping in a separate hash map. Good luck!

Limiting yourself to maps could overcomplicate this somewhat. If I understand this correctly, the data structure is completely undefined at compile time. In that case perhaps a simpler way to implement it is as a vector of hash-key-value triples, like this:
#include "stdafx.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class HashKeyValue
{
private:
string hash;
string key;
string value;
public:
HashKeyValue() {}
HashKeyValue(string h, string k, string v)
{
hash = h;
key = k;
value = v;
}
string getHash() { return hash; }
string getKey() { return key; }
string getValue() { return value; }
};
class my_table
{
private:
vector<HashKeyValue> hkv;
public:
my_table() {}
void set(string h, string k, string v)
{
hkv.push_back(HashKeyValue(h, k, v));
}
string getV(string h, string k)
{
for (unsigned int i = 0; i < hkv.size(); i++)
{
if (hkv[i].getHash() == h && hkv[i].getKey() == k)
return hkv[i].getValue();
}
return "n/a";
}
string getByColValue(string col1, string val, string col2)
{
string hash;
int got = 0;
for (unsigned int i = 0; i < hkv.size() && !got; i++)
{
if (hkv[i].getKey() == col1 && hkv[i].getValue() == val)
{
hash = hkv[i].getHash();
got = 1;
}
}
if (got)
{
for (unsigned int i = 0; i < hkv.size(); i++)
{
if (hkv[i].getHash() == hash && hkv[i].getKey() == col2)
return hkv[i].getValue();
}
return "n/a";
}
else return "n/a";
}
};
int main()
{
my_table m;
m.set("1", "Tenor", "1D");
m.set("3", "Tenor", "3M");
m.set("3", "Rate", "1.6%");
cout << "get-1-Tenor(1D): " << m.getV("1", "Tenor") << endl;
cout << "get-1-Alto(n/a): " << m.getV("1", "Alto") << endl;
cout << "get-3-Rate(1.6%): " << m.getV("3", "Rate") << endl;
cout << "getBCV-Tenor-3M-Rate(1.6%): " << m.getByColValue("Tenor", "3M", "Rate") << endl;
return 0;
}
Hopefully getByColValue() makes sense; it first looks up the hash, then looks up the Rate for that hash. The hash is what relates each key-value pair to others on the same row. It shouldn't be too tricky to change getByColValue() to return a vector<string> instead, for the getByColValue("Type","Forward","Rate") case: just make hash a vector<string> instead, define the return type as another vector<string>, and a few other tweaks.
This also makes the implementation of getRow() fairly trivial; just loop over hkv where hash==rowid and bung the key/value pairs (or just the values) into a vector.

Related

Using STL functions with a struct?

I've recently read up on STL functions in C++. I understand the basic uses of the functions, but I am struggling getting them to use member variables of a struct.
I have this struct:
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
Basically, I insert Apples into a vector storing random weights and random color. Now, I want to use a count_if function to determine how many apples are greater than a given weight. I want to convert a function like this:
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
to a count_if() version (this does not work):
int cnt = count_if(crate.begin(), crate,end(), isGreater())
With isGreater() being like this:
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
What I don't understand about STL functions and a struct is how to use the member variables inside of the struct with the STL functions. I'm not sure what to pass inside of the STL function, either. Would it be better to use a lambda function in this case? If so, why?
Here is all the current code, if it doesn't make sense:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <deque>
#include <string>
using namespace std;
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
int main()
{
srand(time(nullptr));
const double minWeight = 8.;
const double maxWeight = 3.;
cout << "Input crate size: ";
int size;
cin >> size;
vector <Apples> crate(size);
for(auto it = crate.begin(); it != crate.end(); ++it)
{
it->weight = minWeight + static_cast<double>(rand())/RAND_MAX*(maxWeight - minWeight);
it->color = rand() % 2 == 1 ? "green" : "red";
}
cout << "Enter weight to find: ";
double toFind;
cin >> toFind;
//this is what I want to convert to count if
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
std::count_if takes unary predicate as the third argument. In this case unary predicate is a function taking one object and returning true if object matches find criterion or false if not.
Since your criterion depends on toFind, it seems more laconic to use lambda capturing toFind:
int cnt = count_if(crate.begin(), crate.end(), [toFind](const Apple& apple) {
return it->weight > toFind;
});
If you want a standalone function, you can use:
bool isGreater(double toFind, const Apple& apple) {
return it->weight > toFind;
}
...
int cnt = count_if(crate.begin(), crate.end(),
std::bind(&isGreater, toFind, std::placeholders::_1));
Note, that you don't need to call function, you need to pass it:
int cnt = count_if(crate.begin(), crate,end(), isGreater())
// ^^ remove parentheses
you are not storing the apples in the vector.
you have to initialize inside a loop each apple and then store them in the vector.
crate.push_back(newApple).
so run a loop from 0 to size.
inside that loop initialize new apples and give them weights and colors
then push_back in vector:
for(int i = 0; i < size ++i)
{
apples newApple;
newApple.weight = ...;
newApple.color = ...;
crate.push_back(newApple);
}
This is usually accomplished by creating a "functor" class, a class whose objects can be called like a function. Each instance call hold the reference weight:
struct IsGreater {
double w;
IsGreater(double weight) : w{weight} {}
bool operator()(const Apples& A) const {
return A.weight > w;
}
};
Then we just need to create an instance of the class holding the reference weight and pass it to count_if:
const int count = std::count_if(crate.begin(), crate.end(), IsGreater(toFind));
You can avoid creating an explicit class using a lambda:
const int count = std::count_if(crate.begin(), crate.end(),
[=](const Apples& A) -> bool {
return A.weight > toFind;
});
Here the reference value toFind is captured by value.

How to provide the values corresponding to the placeholders in soci?

I'm using soci 3.2.2. I'm looking for a way to provide multiple placeholders and corresponding values.
std::vector<std::string> vs;
vs.push_back("key1");
vs.push_back("key2");
sql << "select * from mytable as t where t.field1 = :f1 and t.field2 = :f2", use(vs[0]), use(vs[1]);
Let's say my table has many columns. For example field1, field2, ...
The placeholders :f1 and :f2 is corresponding to filed1 and field2. The number of placeholders changes dynamically. So I create query string that contains placeholders dynamically. It is a simple string manipulation. So far, so good. However, I couldn't find a way to provide multiple values that is corresponding to placeholders. use(vs[0]), use(vs[1]), ... are not string but C++ code. So I can't generate it on run time.
I found a way to solve it but it's not elegant. The way is that giving up to use the function use() and insert the actual value such as "key1" directly using string manipulation. It's not safe. I need to implement to avoid SQL injection. It is achieved by use() function.
I'm looking for a better way.
Updated
Solution1 use Core interface
Thanks to the following comments:
https://github.com/SOCI/soci/issues/354#issuecomment-115658512
https://github.com/SOCI/soci/issues/354#issuecomment-115662758
the problem has been solved using 'Core' interface.
http://soci.sourceforge.net/doc/3.2/interfaces.html
Here is the code using 'Core' interface:
session sql(sqlite3, "./test");
std::vector<std::string> qs { "v1", "v2", "v3" }; // determined on run time
int count;
// Create query string dynamically
std::stringstream ss;
ss << "select count(*) from mytable as t where t.field1 = :f1";
for (std::size_t i = 1; i < qs.size(); ++i) {
ss << " and t.field" << i+1 << " = :f" << i+1;
}
// Give the values corresponding to the placeholders in the query string
statement st(sql);
for (auto const& e : qs) {
st.exchange(use(e));
}
st.exchange(into(count));
st.alloc();
st.prepare(ss.str());
st.define_and_bind();
st.execute(true);
std::cout << count << std::endl;
Solution2 define custom mapping
std::vector is reserved by the soci library. I need to define teh different type. MyVectorOfStrings is that. Then define the custom conversion using type_conversion class template specialization.
#include <soci.h>
#include <sqlite3/soci-sqlite3.h>
#include <iostream>
using namespace soci;
struct MyVectorOfStrings : public std::vector<std::string> {
using std::vector<std::string>::vector;
};
namespace soci
{
template<>
struct type_conversion<MyVectorOfStrings>
{
typedef values base_type;
static void from_base(values const& v, indicator /* ind */, MyVectorOfStrings &p)
{}
static void to_base(const MyVectorOfStrings& p, values& v, indicator& ind) {
for (auto s : p) v << s;
ind = i_ok;
}
};
}
int main()
{
try {
session sql(sqlite3, "./test");
MyVectorOfStrings qs { "v1", "v2", "v3" }; // determined on run time
int count;
sql << "select count(*) from mytable as t where t.field1 = :f1 and t.field2 = :f2 and t.field3 = :f3", use(qs), into(count);
std::cout << count << std::endl;
}
catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << '\n';
}
}
(As you have also asked this question on SOCI#GitHub, I copied my answer from there).
AFAIU, you want to pass vector<string> into query for, let's call it, vertical or column-wise expansion.
AFAICT, it is not possible, vector<T> can be used with, again, horizontal or row-wise, expansion as a row data carrier.
Typically, the protocol is that number of placeholders must match number of use occurrences.
User-defined data and ORM is an exception, where, N placeholders match 1 use occurrence.
You may try ORM with generated placeholders
namespace soci
{
template<>
struct type_conversion<MyVectorOfStrings>
{
typedef values base_type;
static void from_base(values const & v, indicator /* ind */, MyVectorOfStrings & p)
{ ... }
static void to_base(const MyVectorOfStrings & p, values & v, indicator & ind)
{
int i = 0;
for (auto s : p)
{
// generate placeholders from V0...Vn where n is size of p
v.set("V" + std::to_string(i);, s);
i++;
}
ind = i_ok;
}
};
}
Then try something along these lines:
MyVectorOfStrings p = ...;
std::string query = "select * from mytable as t where ";
int i = 0;
for (auto s : p)
{
if (i > 0) query += " and ";
std::string si = std::to_string(i);
query += "t.field" + si + "=:f" + si;
}
sql << query, use(p);
TBH, I have never tried to run it, so no idea if it would even work :-)
this is simple example:
std::vector<std::string> ids;
soci::session s;
auto p = (s.prepare << "select id from mytable as t where false ");
for (auto & i : ids)
{
p << " or id = :v", use(i);
}
soci::statement stmt{p};
stmt.execute();

Open hashing, add element, and find element using iterator

I have an open hash table using the STL.
typedef std::list<int> LIST;
typedef std::vector<LIST> HASH_TABLE;
I initialized the hash table by filling it with empty lists.
LIST mt_list;
HASH_TABLE hTable;
hTable.assign(7, mt_list);
Now if I want to add an int to my table based on:
hKey = (value*value) % 7;
and I use
hTable[hKey].push_back(value);
It should work right? I can't get it to work.
void addValue(int value){
if(val_find(value)){
std::cout << "WARNING: duplicate input: " << value << std::endl;
}
else{
calc_hash_bucket(value); //set hKey
hTable[hKey].push_back(value); //push value into list
}
}
The code above does not add the element to any of the lists within the vector.
Also, when I want to use an iterator to traverse the vector and the lists within the vector, how do I get one element at a time from a list so I can find a particular value that may or may not already be in the list?
This is what I have for finding a value within the hash table:
bool val_find(int value){
if(mt_hash()){
return false;
}
else{
for(HASH_ITER h_iter = hTable.begin(); h_iter != hTable.end(); ++h_iter){
for(LIST_ITER l_iter = h_iter->begin(); l_iter != h_iter->end(); ++l_iter){
if(*l_iter == value){
return true;
}
}
}
}
return false;
}
I'm stumped. I don't understand why it won't add the value to any of the lists.
I feel I should mention this is all in a header file and part of a class that I created. (I don't know if that matters)
Edit: The warning statement does not print. To answer questions, the mt_hash() function checks to see if the hash table is empty and I have checked it several times to make sure it outputs correctly. I fixed the hTable_1 vs hTable difference, they are the same thing. I just forgot to change it when I put it into the question.
bool mt_hash(void){ //is hash table empty?
for(unsigned int i = 0; i < hTable.size(); ++i){
if(!hTable.at(i).empty()){ //if not empty return false
return false;
}
}
return true; //else return true
}
Thanks,
Zach
As Pradhan points out, there is a quite a bit missing. What is the implementation of mt_hash()? Are hTable_1 and hTable the same object?
Below, I've taken your code above, and placed them in a struct with the implied functionality included. Note three changes: hTable replaces hTable_1 in val_find(); addValue() uses a local variable to store the hash key; and mt_hash() is implemented by keeping a simple element count.
#include <list>
#include <vector>
#include <iostream>
#include <iomanip>
struct open_hash {
typedef std::list<int> LIST;
typedef std::vector<LIST> HASH_TABLE;
typedef LIST::const_iterator LIST_ITER;
typedef HASH_TABLE::const_iterator HASH_ITER;
HASH_TABLE hTable;
int nbins;
int elem_count;
explicit open_hash(int nbins_): nbins(nbins_), elem_count(0) {
init_hash();
}
void init_hash() {
LIST mt_list;
hTable.assign(nbins, mt_list);
}
int hash_bucket(int value) const {
return (value*value)%nbins;
}
bool mt_hash() const {
return elem_count==0;
}
bool val_find(int value) const {
if (mt_hash()) {
return false;
}
for (HASH_ITER h_iter = hTable.begin(); h_iter != hTable.end(); ++h_iter){
for (LIST_ITER l_iter = h_iter->begin(); l_iter != h_iter->end(); ++l_iter){
if (*l_iter == value) {
return true;
}
}
}
return false;
}
void addValue(int value) {
if (val_find(value)) {
std::cout << "WARNING: duplicate input: " << value << std::endl;
}
else {
int hKey=hash_bucket(value);
hTable[hKey].push_back(value); //push value into list
++elem_count;
}
}
};
int main() {
open_hash H(7);
std::vector<int> vals={3,1,9,2,10,4,3};
for (int v: vals) {
H.addValue(v);
}
for (int i=1; i<=10; ++i) {
std::cout << "val_find(" << i << "):\t" << std::boolalpha << H.val_find(i) << "\n";
}
}
This produces expected output:
WARNING: duplicate input: 3
val_find(1): true
val_find(2): true
val_find(3): true
val_find(4): true
val_find(5): false
val_find(6): false
val_find(7): false
val_find(8): false
val_find(9): true
val_find(10): true
I suspect the original problem lies in addValue() and val_find() referring to different hash objects, or a problem in mt_hash() misreporting that the table is empty when in fact it is not.
The answer to the this problem is to create the class object of type HASH_TABLE in the main cpp file and then pass it by reference into the function (getCmd) that calls all of the commands and i/o.
I was calling the "getCmd" function in main() and that function (EVERY time it is called) creates a NEW instance of the HASH_TABLE class, effectively "replacing" the previous object with a new, empty object. (though I suspect it did not actually replace the previous object. I think the previous object was still taking up memory but it was not being used)
I didn't post the code for the problem area because I didn't know where the problem was.
Thanks for all your help!

Where is the operator "<" used in this sample?

I tried STL sample program using "map".
http://ideone.com/LB8xvh
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
class ItemName
{
char name[80];
public:
ItemName(char *s) { strcpy(name, s); }
char *get() { return name; }
};
bool operator<(ItemName a, ItemName b)
{
return strcmp(a.get(), b.get()) < 0;
}
class ItemObj
{
char str[80];
public:
ItemObj(char *s) { strcpy(str, s); }
char *get() { return str; }
};
char itemdata[][80] = {
"potion", "heal HP",
"key", "unlock a door",
"lamp", "light",
};
int main() {
map<ItemName, ItemObj> items;
for(int i=0; i<3; i++) {
items.insert(
pair<ItemName, ItemObj>(
ItemName(itemdata[i*2]),
ItemObj(itemdata[i*2+1]))); // ***** pair *****
}
map<ItemName, ItemObj>::iterator p;
char str[80];
const int kMaxLoop = 5;
int nLoop = 0;
while(nLoop < kMaxLoop) {
cout << "> ";
cin >> str;
p = items.find(str);
if(p != items.end() ) {
cout << p->second.get() << endl;
} else {
cout << "unknown item." << endl;
}
nLoop++;
}
return 0;
}
In this example, I am not quite sure where the operator "<" is used.
If I comment out the definition of the operator "<", I receive lots of errors.
std::map has a parameter to specify how to compare elements in the map (needed because a map always maintains its contents sorted in order by key). By default, that's std::less<T>.
std::less<T>, in turn, will do the comparison using operator<.
You can create a map of items for which operator< isn't defined, but to do it you need to specify the comparison function/functor explicitly.
That said: your ItemData and ItemObj are both really just doing things that std::string can already do. You could reduce most of the code above to something like this:
std::map<std::string, std::string> items{
{ "potion", "heal HP" },
{ "key", "unlock a door" },
{ "lamp", "light" }
};
It is used internally by the map to place and find entries. Otherwise, find would have to compare the key you supply it against literally every single other entry one by one and you couldn't iterate the map in key order.
Basically, maps efficiently store elements in order. To do that, they have to have some way to know what the order is, and they do that by calling operator< (unless you specify otherwise).

Checking if Container has Value (c++)

I have a custom class 'team' and one of its attributes is its 'name.' After each 'team' is created, I add it to a vector teamList.
I would like to implement a function that continuously prompts the user for a team name which is not already taken by a team within the teamList. I have the following code:
while (true) {
string newString;
bool flag = true;
getline(cin, newString);
for (int i = 0; i < teamList.size(); i++) {
if (teamList[i].name.compare(newString) == 0) flag = false;
}
if (flag == true) {
return newString;
} else {
cout << "name already taken." << endl;
}
}
However, this code is really ugly; is there a better way to check? Also, a more general question- faced with an issue of ugly code (like this one), what kinds of steps can I take to find a new, cleaner implementation? Thanks.
I would use std::set, which deals with duplicates for you. As an example, you can see that the class is sorted by the string member, and when three are inserted in main, only two stay because two of the insertions have the same string, so they are treated equal.
#include <iostream>
#include <set>
#include <string>
struct SetThing {
SetThing(int value, const std::string &value2) : i(value), s(value2){}
int i;
std::string s;
bool operator<(const SetThing &other) const {
return s < other.s;
}
};
int main() {
std::set<SetThing> s;
s.insert(SetThing(5, "abc"));
s.insert(SetThing(4, "def"));
s.insert(SetThing(6, "abc"));
std::cout << s.size();
}
Now for inserting, you can just reprompt while the second member of the returned pair is false:
do {
//get input
} while (!teamList.insert(somethingBasedOnInput).second);
define an equality operator in team that can compare a team to a string:
bool team::operator==(string s) const
{
return(s==name);
}
Then you can use find:
vector<team>::const_iterator itr = find(teamList.begin(), teamList.end(),
newString);
if(itr!=league.end())
cout << "name already taken" << endl;